camera 点击 af ae 区域 矩阵转换


1. 手指点击 surface 的坐标
给 根布局添加 添加手势监听view, 高通的相机代码

camera.xml

<com.android.camera.ui.RenderOverlay
android:id="@+id/render_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent" />


public class RenderOverlay extends FrameLayout {

//事件分发
@Override
public boolean dispatchTouchEvent(MotionEvent m) {
  if (mGestures != null) {
     mGestures.dispatchTouch(m);
        return true;
   }

  if (mTouchClients != null) {
     boolean res = false;
        for (Renderer client : mTouchClients) {
            res |= client.onTouchEvent(m);
 }
   return res;
 }
   return true;
 }
}

 

public class PreviewGestures{

private static final String TAG = "PreviewGestures";
    private static final int MODE_NONE = 0;
    private static final int MODE_ZOOM = 2;
    private final int FLING_VELOCITY_MIN = 400;
    private Activity mContext;
    private GestureListener mGestureListener;
    private ScaleGestureDetector mScaleDetector;
    private GestureDetector mGestureDetector;
    private int mMode;
    private boolean mEnabled;
    private boolean onScaling = false;

   // listener 
    private GestureDetector.SimpleOnGestureListener mListener = new GestureDetector.SimpleOnGestureListener() {
@Override
public void onLongPress (MotionEvent e) {
if(mGestureListener != null) {
mGestureListener.onLongPress((int) e.getX(), (int) e.getY());
}
        }

@Override
public boolean onSingleTapUp (MotionEvent e) {
// 回调到各个 module
// Tap to focus when pie is not open
if(mGestureListener != null){
mGestureListener.onSingleTapUp(null, (int) e.getX(), (int) e.getY());
}
return true;
}

@Override
public boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (e1 == null) {
// e1 can be null if for some cases.
return false;
}
if (mMode == MODE_ZOOM) return false;

            int deltaX = (int) (e1.getX() - e2.getX());
            int deltaY = (int) (e1.getY() - e2.getY());

            int orientation = 0;

            if (isLeftSwipe(orientation, deltaX, deltaY)) {
if(mGestureListener != null){
mGestureListener.onSwipeRight(e1, e2, distanceX, distanceY);
}
return true;
}
if (isRightSwipe(orientation, deltaX, deltaY)) {
if(mGestureListener != null){
mGestureListener.onSwipeLeft(e1, e2, distanceX, distanceY);
}
return true;
}
if (isUpSwipe(orientation, deltaX, deltaY)) {
if(mGestureListener != null){
mGestureListener.onSwipeUp(e1, e2, distanceX, distanceY);
}
return true;
}
if (isDownSwipe(orientation, deltaX, deltaY)) {
if(mGestureListener != null){
mGestureListener.onSwipeDown(e1, e2, distanceX, distanceY);
}
return true;
}
return false;
}

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if(mGestureListener != null &&
                    (Math.abs(velocityX) > FLING_VELOCITY_MIN || Math.abs(velocityY) > FLING_VELOCITY_MIN) &&
                    !onScaling){
mGestureListener.onFling(e1, e2, velocityX, velocityY);
}
return true;
}

// left -> right
private boolean isLeftSwipe(int orientation, int deltaX, int deltaY) {
switch (orientation) {
case 90:
return deltaY > 0 && Math.abs(deltaY) > 2 * Math.abs(deltaX);
                case 180:
return deltaX > 0 && Math.abs(deltaX) > 2 * Math.abs(deltaY);
                case 270:
return deltaY < 0 && Math.abs(deltaY) > 2 * Math.abs(deltaX);
                default:
return deltaX < 0 && Math.abs(deltaX) > 2 * Math.abs(deltaY);
}
        }

//right -> left
private boolean isRightSwipe(int orientation, int deltaX, int deltaY) {
switch (orientation) {
case 90:
return deltaY < 0 && Math.abs(deltaY) > 2 * Math.abs(deltaX);
                case 180:
return deltaX < 0 && Math.abs(deltaX) > 2 * Math.abs(deltaY);
                case 270:
return deltaY > 0 && Math.abs(deltaY) > 2 * Math.abs(deltaX);
                default:
return deltaX > 0 && Math.abs(deltaX) > 2 * Math.abs(deltaY);
}
        }

//bottom -> top
private boolean isUpSwipe(int orientation, int deltaX, int deltaY) {
switch (orientation) {
case 90:
return deltaX > 0 && Math.abs(deltaX) > 2 * Math.abs(deltaY);
                case 180:
return deltaY < 0 && Math.abs(deltaY) > 2 * Math.abs(deltaX);
                case 270:
return deltaX < 0 && Math.abs(deltaX) > 2 * Math.abs(deltaY);
                default:
return deltaY > 0 && Math.abs(deltaY) > 2 * Math.abs(deltaX);
}
        }

//top -> bottom
private boolean isDownSwipe(int orientation, int deltaX, int deltaY) {
switch (orientation) {
case 90:
return deltaX < 0 && Math.abs(deltaX) > 2 * Math.abs(deltaY);
                case 180:
return deltaY > 0 && Math.abs(deltaY) > 2 * Math.abs(deltaX);
                case 270:
return deltaX > 0 && Math.abs(deltaX) > 2 * Math.abs(deltaY);
                default:
return deltaY < 0 && Math.abs(deltaY) > 2 * Math.abs(deltaX);
}
        }
    };

    public interface GestureListener{
boolean onSingleTapUp(View v, int x, int y);
        boolean onLongPress(int x, int y);
        void onSwipeUp(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
        void onSwipeDown(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
        void onSwipeLeft(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
        void onSwipeRight(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
        void onScale(ScaleGestureDetector detector);
        void onScaleBegin(ScaleGestureDetector detector);
        void onScaleEnd(ScaleGestureDetector detector);
        void onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
}

public PreviewGestures(Activity ctx) {
mContext = ctx;
mMode = MODE_NONE;
mEnabled = true;
mGestureDetector = new GestureDetector(mListener);
mScaleDetector = new ScaleGestureDetector(mContext, new ScaleGestureDetector.OnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector detector) {
if(mGestureListener != null){
mGestureListener.onScale(detector);
onScaling = true;
}
return true;
}

@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
if(mGestureListener != null){
mGestureListener.onScaleBegin(detector);
}
return true;
}

@Override
public void onScaleEnd(ScaleGestureDetector detector) {
if(mGestureListener != null){
mGestureListener.onScaleEnd(detector);
onScaling = false;
}
            }
        });
}

public void setGestureListener(GestureListener listener){
mGestureListener = listener;
}

public void setEnabled(boolean enabled) {
mEnabled = enabled;
}

public boolean isEnabled() {
return mEnabled;
}

   //接收到事件 去处理
public boolean dispatchTouch(MotionEvent m) {
mGestureDetector.onTouchEvent(m);
mScaleDetector.onTouchEvent(m);
        switch (m.getAction()){
case MotionEvent.ACTION_DOWN:{
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:{
onScaling = false;
                break;
}
case MotionEvent.ACTION_MOVE:{
break;
}
        }
return true;
}
}

//2. 每个监听的module

class  captureModule implements  GestureListener{

    //点击了
    public void onSingleTapUp(View v, int x, int y) {
        //解锁
        if (isAfAeLocked) {
            lockAEAF(false);
        }

        mState = STATE_WAITING_TOUCH_FOCUS;
        int orientation = mOneCamera.getDisplayOrientation(mOrientation);
        mMainHandler.removeMessages(CANCEL_TOUCH_FOCUS);
        //
	mOneCameraParamController.triggerFocusAtPoint(x, y, mCurrentZoom, getSurfaceViewSize(), orientation);
        Message message = mMainHandler.obtainMessage(CANCEL_TOUCH_FOCUS);
        mMainHandler.sendMessageDelayed(message, CANCEL_TOUCH_FOCUS_DELAY);
    }
 }


// 3. 具体的设置转换区域

 public class OneCameraParamController {

    private MeteringRectangle[] mAFRegions;
    private MeteringRectangle[] mAERegions;
    private int mControlAFMode = CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE;
    private final MeteringRectangle[] ZERO_WEIGHT_3A_REGION = new MeteringRectangle[]{
            new MeteringRectangle(0, 0, 0, 0, 0)};

   //将点击的位置坐标  和 当前变焦倍数  surfaceViewsize传递过去
   public void triggerFocusAtPoint(float x, float y,double zoom,Point p,int displayOrientation){
        
         Rect cropRegion = new Rect();
         //裁剪区域
         cropRegionForZoom(cropRegion,zoom);
         if (cropRegion.top < 0
                || cropRegion.bottom < 0
                || cropRegion.left < 0
                || cropRegion.right < 0){
            Log.d(TAG, "crop region exit error exception,param <0 ");
            return;
        }
        //获取预览 surface 的宽高
        int width = p.x;
        int height = p.y;
        // 点击的x y , surfaceView 的w h, 区域比例, 方向
        mAFRegions = afaeRectangle(x, y, width, height, 1f, cropRegion,displayOrientation);
        mAERegions = afaeRectangle(x, y, width, height, 1.5f, cropRegion,displayOrientation);
        autoFocusTrigger();
    }
    
    private MeteringRectangle[] afaeRectangle(float x, float y, int width, int height,
                                              float multiple, Rect cropRegion,int displayOrientation) {
        //默认效果区域, 1/8 * multiple
        float side = Math.max(width, height) / 8f * multiple;
        RectF meteringRegionF = new RectF(x - side / 2f, y - side / 2f, x + side / 2f, y + side / 2f);

        // inverse of matrix1 will translate from touch to (-1000 to 1000), which is camera1
        // coordinates, while accounting for orientation and mirror
        Matrix matrix1 = new Matrix();
        
        //视图坐标系到 预览坐标系
        CameraUtils.prepareMatrix(matrix1, !mOneCamera.isBackCamera(), displayOrientation, width, height);
        matrix1.invert(matrix1);

        //预览坐标系 转换 camera2 坐标系
        // inverse of matrix2 will translate from (-1000 to 1000) to camera 2 coordinates
        Matrix matrix2 = new Matrix();
        matrix2.preTranslate(-cropRegion.width() / 2f, -cropRegion.height() / 2f);
        matrix2.postScale(2000f / cropRegion.width(), 2000f / cropRegion.height());
        matrix2.invert(matrix2);

        //转换
        matrix1.mapRect(meteringRegionF);
        matrix2.mapRect(meteringRegionF);

        //获取到转换后的实际af/ae 的区域大小
        Rect meteringRegion = new Rect((int) meteringRegionF.left, (int) meteringRegionF.top,
                (int) meteringRegionF.right, (int) meteringRegionF.bottom);

        //边界验证
        meteringRegion.left = CameraUtils.clamp(meteringRegion.left, cropRegion.left,
                cropRegion.right);
        meteringRegion.top = CameraUtils.clamp(meteringRegion.top, cropRegion.top,
                cropRegion.bottom);
        meteringRegion.right = CameraUtils.clamp(meteringRegion.right, cropRegion.left,
                cropRegion.right);
        meteringRegion.bottom = CameraUtils.clamp(meteringRegion.bottom, cropRegion.top,
                cropRegion.bottom);

        //生成需要实际 camera2 af/ae 的区域大小
        MeteringRectangle[] meteringRectangle = new MeteringRectangle[1];
        if ((meteringRegion.right - meteringRegion.left == 0) && meteringRegion.bottom - meteringRegion.top == 0) {
            meteringRectangle[0] = new MeteringRectangle(cropRegion, 1);
        } else {
            meteringRectangle[0] = new MeteringRectangle(meteringRegion, 1);
        }
        return meteringRectangle;
    }

     private void cropRegionForZoom(Rect cropRegion,double zoom) {
        //获取当前相机的可用区域的大小
        Rect activeRegion = getSensorActiveArraySize(mOneCamera);
        if (activeRegion == null){
            return;
        }

        //获取中心点
        int xCenter = activeRegion.width() / 2;
        int yCenter = activeRegion.height() / 2;

        //根据变焦倍数 重新计算
        int xDelta = (int) (activeRegion.width() / (2 * zoom));
        int yDelta = (int) (activeRegion.height() / (2 * zoom));

        //裁剪出具体的可视区域
        cropRegion.set(xCenter - xDelta, yCenter - yDelta, xCenter + xDelta, yCenter + yDelta);
    }

    private Rect getSensorActiveArraySize(OneCamera mOneCamera){
        if(mOneCamera.getCharacteristics() == null){
            return  new Rect(0,0,3264,2448);
        }
        //获取可用区域
        return mOneCamera.getCharacteristics().get(CameraCharacteristics
                .SENSOR_INFO_ACTIVE_ARRAY_SIZE);
    }
    

3.  将区域设置

 private void autoFocusTrigger() {
       CaptureRequest.Builder builder = getPreviewRequestBuilder();
       if(builder == null){
           return;
       }
       //设置对焦模式
       mControlAFMode = CaptureRequest.CONTROL_AF_MODE_AUTO;
       builder.set(CaptureRequest.CONTROL_AF_MODE, mControlAFMode);
       //设置af ae 区域
       applySettingsForAutoFocus(builder);
       //下发 builder中的指令参数
       mOneCamera.startRepeatingRequest();
    }

    public void applySettingsForAutoFocus(CaptureRequest.Builder builder) {
        builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
        applyAFRegions(builder);
        applyAERegions(builder);
    }

    private void applyAFRegions(CaptureRequest.Builder request) {
        if (mControlAFMode == CaptureRequest.CONTROL_AF_MODE_AUTO) {
            request.set(CaptureRequest.CONTROL_AF_REGIONS, mAFRegions);
        } else {
            request.set(CaptureRequest.CONTROL_AF_REGIONS, ZERO_WEIGHT_3A_REGION);
        }
    }

    private void applyAERegions(CaptureRequest.Builder request) {
        if (mControlAFMode == CaptureRequest.CONTROL_AF_MODE_AUTO) {
            request.set(CaptureRequest.CONTROL_AE_REGIONS, mAERegions);
        } else {
            request.set(CaptureRequest.CONTROL_AE_REGIONS, ZERO_WEIGHT_3A_REGION);
        }
    }




    }

CameraUtils

class CameraUtils{

  public static void prepareMatrix(Matrix matrix, boolean mirror, int displayOrientation,
            int viewWidth, int viewHeight) {
        // Need mirror for front camera.
        matrix.setScale(mirror ? -1 : 1, 1);
        // This is the value for android.hardware.Camera.setDisplayOrientation.
        matrix.postRotate(displayOrientation);
        // Camera driver coordinates range from (-1000, -1000) to (1000, 1000).
        // UI coordinates range from (0, 0) to (width, height).

        //视图坐标系 转换到   预览坐标系 范围
        matrix.postScale(viewWidth / 2000f, viewHeight / 2000f);
        //视图坐标系 转换到   预览坐标系 原点
        matrix.postTranslate(viewWidth / 2f, viewHeight / 2f);
    }



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

    public static float clamp(float x, float min, float max) {
        if (x > max) return max;
        if (x < min) return min;
        return x;
    }

} 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

空白的泡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值