C# Camera2 实现扫描识别二维码及Texture预览

前几天做了电脑端二维码识别,突然想在手机上做一个二维码识别功能,发现过程还是有点麻烦,问题主要出现在要实现预览,预览界面的视频比例问题,最终也未能完美解决,实际效果尚可

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:showIn="@layout/activity_main">

    <Button
        android:id="@+id/btnQRCodeScan"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="扫描二维码" />

    <TextureView
      android:id="@+id/textureQRCodeScan"
      android:layout_gravity="center"
      android:layout_width="match_parent"
      android:layout_height="match_parent" />

    <TextView
      android:id="@+id/textviewQRCodeScan"
      android:visibility="gone"
      android:layout_width="match_parent"
      android:layout_height="match_parent" />
</LinearLayout>

MainActivity代码:

using System;
using Android.App;
using Android.OS;
using Android.Runtime;
using Android.Support.Design.Widget;
using Android.Support.V4.App;
using Android.Support.V4.Content;
using Android.Support.V7.App;
using Android.Views;
using Android.Widget;
using System.Linq;
using static Android.Manifest;
using Android.Content;
using Android.Provider;
using Android.Graphics;
using Android.Hardware.Camera2;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Android.Net.Wifi.Aware;
using Android.Util;

namespace QRCodeScanner
{
    [Activity(Label = "@string/app_name", Theme = "@style/AppTheme.NoActionBar", MainLauncher = true)]
    public class MainActivity : AppCompatActivity
    {
        private static CameraCaptureSession cameraSession = null;
        private static readonly object sessionLocker = new object();

        public class CameraStateListener : CameraDevice.StateCallback
        {
            TextureView textureView = null;

            public CameraStateListener(TextureView texture)
            {
                textureView = texture; 
            }

            public override void OnOpened(CameraDevice cameraDevice)
            {
                var surfaces = new List<Surface> { new Surface(textureView.SurfaceTexture) };
                var createCaptureSessionCallback = new CameraCaptureSessionStateListener(cameraDevice, textureView);
                cameraDevice.CreateCaptureSession(surfaces, createCaptureSessionCallback, null);
            }

            public override void OnDisconnected(CameraDevice cameraDevice)
            {
                
            }

            public override void OnError(CameraDevice cameraDevice, CameraError error)
            {
                
            }
        }

        public class CameraCaptureSessionStateListener : CameraCaptureSession.StateCallback
        {
            private CameraDevice cameraDevice = null;
            private TextureView textureView = null;

            public CameraCaptureSessionStateListener(CameraDevice camera, TextureView texture)
            {
                cameraDevice = camera;
                textureView = texture;
            }

            public override void OnConfigureFailed(CameraCaptureSession session)
            {
                //do nothing
            }

            public override void OnConfigured(CameraCaptureSession session)
            {
                var requestBuilder = cameraDevice.CreateCaptureRequest(CameraTemplate.Preview);
                requestBuilder.AddTarget(new Surface(textureView.SurfaceTexture));
                var request = requestBuilder.Build();

                lock (sessionLocker)
                {
                    cameraSession = session;
                    cameraSession.SetRepeatingRequest(request, null, null);
                }
            }
        }

        private void stopCapture()
        {
            lock (sessionLocker)
            {
                if (cameraSession != null)
                {
                    cameraSession.StopRepeating();
                    cameraSession = null;
                }
            }
        }

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
            SetContentView(Resource.Layout.activity_main);

            Android.Support.V7.Widget.Toolbar toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
            SetSupportActionBar(toolbar);

            FloatingActionButton fab = FindViewById<FloatingActionButton>(Resource.Id.fab);
            fab.Click += FabOnClick;

            var btnQRCodeScan = FindViewById<Button>(Resource.Id.btnQRCodeScan);
            btnQRCodeScan.Click += BtnQRCodeScan_Click;

            var timer = new Timer(new TimerCallback(delegate (object state) 
            {
                this.RunOnUiThread(() => 
                {
                    var textureQRCodeScan = FindViewById<TextureView>(Resource.Id.textureQRCodeScan);
                    var textviewQRCodeScan = FindViewById<TextView>(Resource.Id.textviewQRCodeScan);
                    var image = textureQRCodeScan.Bitmap;
                    if (image == null)
                        return;

                    lock (sessionLocker)
                    {
                        if (cameraSession == null)
                            return;
                    }

                    var task = new Task(()=> {

                        lock (sessionLocker)
                        {
                            if (cameraSession == null)
                                return;
                        }

                        var sw = new System.Diagnostics.Stopwatch();
                        sw.Start();

                        var data = ReadQRCode(image);

                        sw.Stop();

                        System.Diagnostics.Debug.Print("time cost:" + sw.ElapsedMilliseconds);

                        if (data.Length > 0)
                        {
                            this.RunOnUiThread(() => {
                                stopCapture();
                                System.Diagnostics.Debug.Print("data:" + data);
                                FindViewById<TextureView>(Resource.Id.textureQRCodeScan).Visibility = ViewStates.Gone;
                                FindViewById<TextView>(Resource.Id.textviewQRCodeScan).Text = data;
                                FindViewById<TextView>(Resource.Id.textviewQRCodeScan).Visibility = ViewStates.Visible;
                                FindViewById<Button>(Resource.Id.btnQRCodeScan).Visibility = ViewStates.Visible;
                            });
                            
                        }

                    });

                    task.Start();

                });
            }), null, 0, 200);
        }

        public override bool OnCreateOptionsMenu(IMenu menu)
        {
            MenuInflater.Inflate(Resource.Menu.menu_main, menu);
            return true;
        }

        public override bool OnOptionsItemSelected(IMenuItem item)
        {
            int id = item.ItemId;
            if (id == Resource.Id.action_settings)
            {
                return true;
            }

            return base.OnOptionsItemSelected(item);
        }

        private void FabOnClick(object sender, EventArgs eventArgs)
        {
            View view = (View) sender;
            Snackbar.Make(view, "Replace with your own action", Snackbar.LengthLong)
                .SetAction("Action", (Android.Views.View.IOnClickListener)null).Show();
        }

        private void BtnQRCodeScan_Click(object sender, EventArgs e)
        {
            if (Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.M)
            {
                var permission = new string[] { Permission.Camera, "android.hardware.camera.autofocus" };
                var requestPermissions = permission.Where(p => ContextCompat.CheckSelfPermission(this, p) != Android.Content.PM.Permission.Granted).ToList();
                if (requestPermissions.Count > 0)
                    ActivityCompat.RequestPermissions(this, requestPermissions.ToArray(), 1);
            }

            var textureQRCodeScan = FindViewById<TextureView>(Resource.Id.textureQRCodeScan);
            textureQRCodeScan.Visibility = ViewStates.Visible;
            var textviewQRCodeScan = FindViewById<TextView>(Resource.Id.textviewQRCodeScan);
            textviewQRCodeScan.Visibility = ViewStates.Gone;
            textviewQRCodeScan.Text = string.Empty;
            var btnQRCodeScan = FindViewById<Button>(Resource.Id.btnQRCodeScan);
            btnQRCodeScan.Visibility = ViewStates.Gone;

            var cameraManager = (Android.Hardware.Camera2.CameraManager)GetSystemService(Context.CameraService);
            var cameraID = cameraManager.GetCameraIdList()[0];
            var characteristics = cameraManager.GetCameraCharacteristics(cameraID);
            var map = characteristics.Get(CameraCharacteristics.ScalerStreamConfigurationMap) as Android.Hardware.Camera2.Params.StreamConfigurationMap;
            var jpegSizes = map.GetOutputSizes((int)ImageFormatType.Jpeg);
            var sizes = new List<KeyValuePair<int, int>>();
            for (var i = 0; i < jpegSizes.Length; i++)
            {
                sizes.Add(new KeyValuePair<int, int>(jpegSizes[i].Width, jpegSizes[i].Height));
                System.Diagnostics.Debug.Print(jpegSizes[i].Width.ToString() + "X" + jpegSizes[i].Height.ToString());
            }

            var metrics = new DisplayMetrics();
            WindowManager.DefaultDisplay.GetMetrics(metrics);
            
            var screenWidth = metrics.WidthPixels;
            var screenHeight = metrics.HeightPixels;

            var ratedSizes = sizes.Select(x => new { size = x, rate = (float)x.Key / (float)x.Value })
                .Select(x => new { size = x, differ = Math.Abs(x.rate - (float)screenWidth / (float)screenHeight) }).ToList();

            var bestSize = ratedSizes.Where(x => x.differ == ratedSizes.Min(y => y.differ)).First().size.size;

            int videoWidth = bestSize.Key;
            int videoHeight = bestSize.Value;

            var textureWidth = textureQRCodeScan.MeasuredWidth;
            var textureHeight = textureQRCodeScan.MeasuredHeight;

            //以高为基准进行缩放,宽度未超出,则以高为基准进行缩放
            if (videoWidth * textureHeight / videoHeight > textureWidth)
            {
                textureQRCodeScan.LayoutParameters.Width = videoWidth * textureHeight / videoHeight;
                textureQRCodeScan.LayoutParameters.Height = textureQRCodeScan.MeasuredHeight;
            }
            //以宽为基准进行缩放
            else
            {
                textureQRCodeScan.LayoutParameters.Width = textureQRCodeScan.MeasuredWidth;
                textureQRCodeScan.LayoutParameters.Height = textureWidth * videoHeight / videoWidth;
            }

            cameraManager.OpenCamera(cameraID, new CameraStateListener(textureQRCodeScan), null);

        }

        /// <summary>
        /// 生成二维码
        /// </summary>
        /// <param name="data"></param>
        /// <param name="height"></param>
        /// <param name="width"></param>
        /// <param name="margin"></param>
        /// <returns></returns>
        public static byte[] CreateQRCode(string data, int height = 100
                        , int width = 100, int margin = 0)
        {
            byte[] bytes = null;
            var barcodeWriter = new ZXing.Android.BarcodeWriter()
            {

                Format = ZXing.BarcodeFormat.QR_CODE,
                Options = new ZXing.QrCode.QrCodeEncodingOptions()
                {
                    Height = height,
                    Width = width,
                    Margin = margin
                }
            };
            using (var image = barcodeWriter.Write(data))
            {
                using (var stream = new System.IO.MemoryStream())
                {
                    image.Compress(Bitmap.CompressFormat.Png, 100, stream);
                    bytes = stream.ToArray();
                }
            }
            return bytes;
        }

        /// <summary>
        /// 识别二维码
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static string ReadQRCode(Bitmap image)
        {
            var result = string.Empty;
            var barcodeReader = new ZXing.Android.BarcodeReader();
            var decoded = barcodeReader.Decode(image);
            if (decoded != null)
                result = decoded.Text;
            return result;
        }
    }
}

 

posted on 2021-08-02 17:23  空明流光  阅读(334)  评论(0编辑  收藏  举报

导航