如何在相机对焦时实现视觉指示器 [英] How to implement visual indicator when camera is focused

查看:39
本文介绍了如何在相机对焦时实现视觉指示器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当用户为我的应用程序中的相机页面手动对焦(点按对焦)时,我想显示一个基本圆圈.

I want to display a basic circle when the user focuses manually (tap to focus) for the camera page in my app.

我已经在下面实现了自动对焦,但我不确定如何在焦点上绘制圆圈并在视图未聚焦时关闭它,以及在相机聚焦时继续重新绘制它.指示器不应该是最终照片的一部分,只是作为用户何时对焦或未对焦的指南.

I already have autofocus implemented below on tap but I'm not sure how to draw the circle on focus and dismiss it when the view becomes unfocused as well keep redrawing it when the camera is focused. The indicator should not be part of the final photo, just as a guide to the user when the camera is focused or not.

这是我目前所拥有的:

public class AutoFocusCallback : Java.Lang.Object, IAutoFocusCallback
{
    public void OnAutoFocus(bool success, Android.Hardware.Camera camera)
    {
        var parameters = camera.GetParameters();
        var supportedFocusModes = parameters.SupportedFocusModes;

        if (string.IsNullOrEmpty(parameters.FocusMode))
        {
            string focusModeContinuous = Android.Hardware.Camera.Parameters.FocusModeContinuousPicture;
            string focusModeAuto = Android.Hardware.Camera.Parameters.FocusModeAuto;

            if (supportedFocusModes != null && supportedFocusModes.Any())
            {
                if (supportedFocusModes.Contains(focusModeContinuous))
                {
                    parameters.FocusMode = focusModeContinuous;
                }
                else if (supportedFocusModes.Contains(focusModeAuto))
                {
                    parameters.FocusMode = focusModeAuto;
                }
            }
        }

        if (supportedFocusModes != null && supportedFocusModes.Any())
        {
            if (parameters.MaxNumFocusAreas > 0)
            {
                parameters.FocusAreas = null; 
            }

             if (success)
             {
                CameraPage cameraPage = new CameraPage();
                Canvas canvas = new Canvas(); 
                cameraPage.Draw(canvas); 
            }
            camera.SetParameters(parameters);
            camera.StartPreview();
        }
    }
}

public bool OnTouch(Android.Views.View v, MotionEvent e)
{
    if (camera != null)
    {
        var parameters = camera.GetParameters();
        camera.CancelAutoFocus();
        Rect focusRect = CalculateTapArea(e.GetX(), e.GetY(), 1f);

        if (parameters.FocusMode != Android.Hardware.Camera.Parameters.FocusModeAuto)
        {
            parameters.FocusMode = Android.Hardware.Camera.Parameters.FocusModeAuto;
        }
        if (parameters.MaxNumFocusAreas > 0)
        {
            List<Area> mylist = new List<Area>();
            mylist.Add(new Android.Hardware.Camera.Area(focusRect, 1000));
            parameters.FocusAreas = mylist;
        }

        try
        {
            camera.CancelAutoFocus();
            camera.SetParameters(parameters);
            camera.StartPreview();
            camera.AutoFocus(new AutoFocusCallback());
        }
        catch (System.Exception ex)
        {
            Console.WriteLine(ex.ToString());
            Console.Write(ex.StackTrace);
        }
        return true;
    }
    else
    {
        return false;
    } 
}

private Rect CalculateTapArea(object x, object y, float coefficient)
{
    var focusAreaSize = Math.Max(textureView.Width, textureView.Height) / 8; //Recommended focus area size from the manufacture is 1/8 of the image
    int areaSize = focusAreaSize * (int)coefficient;

    int left = clamp(Convert.ToInt32(x) - areaSize / 2, 0, textureView.Width - areaSize);
    int top = clamp(Convert.ToInt32(y) - areaSize / 2, 0, textureView.Height - areaSize);

    RectF rectF = new RectF(left, top, left + areaSize, top + areaSize);
    Matrix.MapRect(rectF);

    return new Rect((int)System.Math.Round(rectF.Left), (int)System.Math.Round(rectF.Top), (int)System.Math.Round(rectF.Right), (int)System.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;
}

public override void Draw(Canvas canvas)
{
    base.Draw(canvas);
    Paint p = new Paint();
    p.Color = Android.Graphics.Color.White; 
    p.SetStyle(Paint.Style.Stroke);
    p.StrokeWidth = 3; 
    canvas.DrawCircle(300, 300, 100, p);
}

例如就像这张照片:

这是我的 Xamarin.Forms Android 应用程序.

This is for my Xamarin.Forms Android app.

这是相机的 Android 源代码的副本,但不确定他们在哪里制作指标:

Here's a copy of the Android Source code for the camera but not sure where they're making the indicator:

https://android.googlesource.com/platform/packages/apps/Camera.git/+/refs/heads/marshmallow-release/src/com/android/camera

没有官方答案的类似 SO 帖子:

Similar SO posts with no official answers:

如何在android中实现点击焦点指示器相机?

实现点击指示器以在相机中对焦 [android]

Android Camera2 - 绘制圆形焦点区域

如您所见,多人有相同的问题但没有答案!如果我还有不清楚的地方,请告诉我?

As you can see, multiple people have the same question but no answers! Please let me know if I'm still being unclear?

推荐答案

我已经在下面实现了自动对焦,但我不确定如何在焦点上绘制圆圈并在视图未聚焦时关闭它,以及在相机聚焦时继续重新绘制它.

I already have autofocus implemented below on tap but I'm not sure how to draw the circle on focus and dismiss it when the view becomes unfocused as well keep redrawing it when the camera is focused.

您发布的图片对我来说看起来像一个系统相机,如果使用 Intent 启动系统相机,我不确定是否可以在系统的相机视图上添加对焦圈.但我猜你可能使用了 SurfaceViewTextureView 来自己托管相机.

The picture you posted looks like a system camera to me, if using Intent to launch the system camera, I'm not sure it is possible to add a focus circle on the system's camera view. But I guess you possibly used a SurfaceView or TextureView to host camera by yourself.

如果是这样,对于您的场景,我认为最简单的方法是在您的布局中放置一个 ImageView 并根据相机更改其 Visibility 并重置其 LayoutParameters焦点状态.图像源需要是透明的,例如我使用了这个.然后我的布局是这样的:

If so, for your scenario, I think the most simply method is to place a ImageView in your layout and change its Visibility and reset its LayoutParameters according to the camera focus state. The image source need to be a transparent one for example I used this one. Then my layout is like so:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
  <TextureView android:id="@+id/textureView"
               android:layout_height="match_parent"
               android:layout_width="match_parent" />

  <Button android:id="@+id/take_photo"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:text="@string/takephoto" />

  <ImageView android:id="@+id/focuscircle"
             android:layout_height="80dp"
             android:layout_width="80dp"
             android:layout_centerInParent="true"
             android:src="@drawable/FocusCircle"
             android:visibility="invisible" />
</RelativeLayout>

我已经稍微修改了您的代码,以使其符合我使用 TextureView 的场景.这是在点击屏幕时使图像可见的代码:

I've modified your code a little bit in order to make it meet my scenario using TextureView. here is the code to make the image visible when tap the screen:

private void _textureView_Touch(object sender, View.TouchEventArgs e)
{
    if (_camera != null)
    {
        var parameters = _camera.GetParameters();
        _camera.CancelAutoFocus();
        Rect focusRect = CalculateTapArea(e.Event.GetX(), e.Event.GetY(), 1f);

        if (parameters.FocusMode != Android.Hardware.Camera.Parameters.FocusModeAuto)
        {
            parameters.FocusMode = Android.Hardware.Camera.Parameters.FocusModeAuto;
        }
        if (parameters.MaxNumFocusAreas > 0)
        {
            List<Area> mylist = new List<Area>();
            mylist.Add(new Android.Hardware.Camera.Area(focusRect, 1000));
            parameters.FocusAreas = mylist;
        }

        try
        {
            _camera.CancelAutoFocus();
            _camera.SetParameters(parameters);
            _camera.StartPreview();
            _camera.AutoFocus(new AutoFocusCallBack());

            MarginLayoutParams margin = new MarginLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent,
            ViewGroup.LayoutParams.WrapContent));
            margin.SetMargins(focusRect.Left, focusRect.Top,
                focusRect.Right, focusRect.Bottom);
            RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(margin);
            layoutParams.Height = 200;
            layoutParams.Width = 200;
            _focusimg.LayoutParameters = layoutParams;
            _focusimg.Visibility = ViewStates.Visible;
        }
        catch (System.Exception ex)
        {
            Console.WriteLine(ex.ToString());
            Console.Write(ex.StackTrace);
        }
        //return true;
    }
    else
    {
        //return false;
    }
}

为了让它消失,您可以在 AutoFocusCallBack 中的 success 状态下进行编码,如下所示:

And to make it disappear you can code in the success state in AutoFocusCallBack like this:

if (success)
{
    Task.Delay(1000);
    Activity1._focusimg.Visibility = ViewStates.Invisible;
}

为了使 ImageView 可从 AutoFocusCallBack 访问,您可以在 xamarin 中将其设为静态:

In order to make the ImageView accessible from AutoFocusCallBack, you can make it a static one in xamarin:

private Android.Hardware.Camera _camera;
private TextureView _textureView;

public static ImageView _focusimg;

protected override void OnCreate(Bundle bundle)
{
    base.OnCreate(bundle);
    SetContentView(Resource.Layout.Main);

    _textureView = (TextureView)this.FindViewById(Resource.Id.textureView);
    _textureView.SurfaceTextureListener = this;
    _textureView.Touch += _textureView_Touch;

    var tpBtn = (Button)this.FindViewById(Resource.Id.take_photo);
    tpBtn.Click += TpBtn_Click;

    _focusimg = (ImageView)this.FindViewById(Resource.Id.focuscircle);
}

每次成功设置焦点时都可以正常工作,但我发现有时当我点击 TextureView 时,它不会触发 _textureView_Touch 事件,我没有'不要深入挖掘这个问题.

This works fine every time when it successfully set focus, but I found that sometimes when I tapped on TextureView, it will not fire the _textureView_Touch event, I didn't deep dig this problem.

我认为的另一种方法是在 Canvas 上动态绘制一个圆圈,就像您在代码中所做的那样,它也有效,但是我发现使用这种方法时我的演示响应很慢,而且我也没有深挖这个问题.反正我觉得第一种使用Image的方法更简单,如果需要demo,请留言.

Another method I thought was to draw a circle dynamically on Canvas like you did in your code, it also worked, but I found my demo responses slow using this method, and I also didn't deep dig this issue. Anyway, I think the first method using a Image is simpler, if a demo is needed, please leave a comment.

这篇关于如何在相机对焦时实现视觉指示器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆