相机聚焦时如何实现可视指示(How to implement visual indicator when camera is focused)

编程入门 行业动态 更新时间:2024-10-27 00:35:14
相机聚焦时如何实现可视指示(How to implement visual indicator when camera is focused)

当用户在我的应用程序中手动聚焦(点击聚焦)相机页面时,我想显示一个基本圆圈。

我已经在下面实现了自动对焦,但我不确定如何在焦点上绘制圆圈并在视图变得不聚焦时将其关闭,并在相机聚焦时保持重绘。 指示器不应该是最终照片的一部分,只是作为相机聚焦与否时用户的指南。

这是我到目前为止所拥有的:

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应用程序。

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

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

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

如何在Android相机中实现tap to focus指示器?

点击实现指示器聚焦在相机[android]

Android Camera2 - 绘制圆形焦点区域

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

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.

Here's what I have so far:

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); }

E.g. like in this picture:

This is for my Xamarin.Forms Android app.

EDIT: 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

Similar SO posts with no official answers:

How to implement tap to focus indicator in android camera?

Implement indicator on tap to focus in camera [android]

Android Camera2 - Draw circle focus area

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

最满意答案

我已经在下面实现了自动对焦,但我不确定如何在焦点上绘制圆圈并在视图变得不聚焦时将其关闭,并在相机聚焦时保持重绘。

您发布的图片看起来像是系统相机,如果使用Intent启动系统相机,我不确定是否可以在系统的相机视图上添加焦点圈。 但我猜你可能自己使用SurfaceView或TextureView来托管相机。

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

<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场景。 这是在点击屏幕时使图像可见的代码:

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状态进行编码,如下所示:

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

为了使ImageView可以从AutoFocusCallBack访问,你可以在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事件,我没有深入挖掘这个问题。

我认为另一种方法是在Canvas动态绘制一个圆圈,就像在代码中一样,它也有效,但我发现我的演示响应使用这种方法很慢,而且我也没有深入挖掘这个问题。 无论如何,我认为使用Image的第一种方法更简单,如果需要演示,请发表评论。

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 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.

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>

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; } }

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; }

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); }

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.

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.

更多推荐

本文发布于:2023-08-07 13:48:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1464908.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:可视   如何实现   指示   相机   implement

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!