【C#】カメラでQRコードとバーコード読取-AForge.NET-

今回はAForge.NETとdelegateを使うやり方。
delegateの練習にも。使わない方法も追記。
前の記事はOpenCvSharpの古いバージョンに依存中。
(CaptureMat➡Bitmap(pictureBox.Imageと解析用)に変換するのがTask内で簡単にできる)

https://qiita.com/A42/items/65139e86f3cb02f89a01

参考にさせていただきました。

https://yossy.penne.jp/wordpress/2024/05/19/c-cammera-net/

## 事前準備
以下を追加
　AForge.Video
　AForge.Video.DirectShow
　ZXing.Net

レイアウト
![1212.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3937966/40a3d03d-7f9a-658b-9d4b-8379174f9f77.png)

pictureBox  x1
button  x2
label1  x1

## カメラ・画像解析の準備
```c#:Form1.cs
using System;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using AForge.Video;
using AForge.Video.DirectShow;
using ZXing;

FilterInfoCollection videoDevices;
VideoCaptureDevice videoSource;
ZXing.BarcodeReader reader = new ZXing.BarcodeReader();
bool code_request = true;
Bitmap bmp;
delegate void DelegateProcess();
Task task;

     private void Form1_Load(object sender, EventArgs e)
     {
        reader.Options = new ZXing.Common.DecodingOptions
        {
            TryHarder = true,
            //読み取るコードの種類
            PossibleFormats = new[] { BarcodeFormat.QR_CODE, BarcodeFormat.EAN_13 }.ToList()
        };
        videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
        if (videoDevices.Count == 0)
        {
            MessageBox.Show("カメラがありません");
            return;
        }
        //外部カメラ優先
        videoSource = new VideoCaptureDevice(videoDevices[videoDevices.Count-1].MonikerString);
        videoSource.NewFrame += new NewFrameEventHandler(video_NewFrame);
        videoSource.Start();
     }

    //pictureBoxに反映
    private void video_NewFrame(object sender, NewFrameEventArgs eventArgs)
    {
        pictureBox1.Image = (Bitmap)eventArgs.Frame.Clone();
    }
```
## Taskとdelegate
非同期⇔UIときたらdelegateかInvoke。
```c#:Form1.cs
    private void Form1_Shown(object sender, EventArgs e)
    {
        //pictureBoxサイズをユーザ側で変更できないならここで一回呼び出す
        bmp= new Bitmap(pictureBox1.Width, pictureBox1.Height);            
        codeRead();
    }
    
    private void codeRead()
    {
        DelegateProcess process = new DelegateProcess(bitMapInput);
        Result result;
        try
        {
            task = Task.Run(() =>
            {
                while (code_request)
                {
                    //そこまでまめにやらなくていい
                    System.Threading.Thread.Sleep(1000);
                    //UI情報取得(pictureBox.ImageをBitmapに変換)
                    this.Invoke(process);
                    result = reader.Decode(bmp);
                    if (result != null)
                    {
                        Console.WriteLine(result.Text);
                        //label(UI)に反映
                        Invoke(new Action(() =>
                        {
                            label1.Text = result.Text;
                        }));
                    }
                }
             });
        }
        catch (Exception e) { Console.WriteLine(e.Message);}
    }
    private void bitMapInput()//UIを取得する処理
    {
        try
        {
            if (pictureBox1.Image != null)
            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.DrawImage(pictureBox1.Image, 0, 0);
            }
        }
        catch(InvalidOperationException e) { Console.WriteLine(e.Message); }
    }
```
## 仕上げ
```c#:Form1.cs
    private async void stopCamera() 
    {
        if (videoSource != null && videoSource.IsRunning)
        {
            code_request = false;
            videoSource.SignalToStop();
            videoSource.WaitForStop();
            
            await task;
        }
    }
    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        stopCamera();
    }
    private void stop_button_Click(object sender, EventArgs e)
    {
        stopCamera();
    }
     private void start_button_Click(object sender, EventArgs e)
    {
        code_request = true;
        videoSource.Start();
        codeRead();
    }
```

## おまけ　delegateを使わないCopyFromScreen
メイン画面をCaptureしUIへ干渉しなければいい。
ただし画像は大きい。位置情報とサイズの考慮でどうにかなる？
➡なりました(追記)
QRコードは問題ないがバーコードだと解像度を上げるかdelegateを使った方が良い。
```c#:
    //全体キャプチャ
    //Rectangle bounds = Screen.PrimaryScreen.Bounds;
    Rectangle bounds = pictureBox1.RectangleToScreen(pictureBox1.ClientRectangle);
    Bitmap bmp = new Bitmap(Bounds.Width, bounds.Height);
    Point upperLeftSource = bounds.Location;
    Result result;
    try
    {
        task = Task.Run(() =>
        {
            while (code_request)
            {
                System.Threading.Thread.Sleep(1000);
                using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
                {
                    // Graphicsオブジェクトを使用して画面全体をキャプチャ
                    //using (Graphics g = Graphics.FromImage(bitmap))
                    //{
                        //g.CopyFromScreen(System.Drawing.Point.Empty, System.Drawing.Point.Empty, bounds.Size);
                    //}
                    using (Graphics g = Graphics.FromImage(bmp))
                    {
                        g.CopyFromScreen(
                        upperLeftSource, System.Drawing.Point.Empty,
                        bounds.Size, CopyPixelOperation.SourceCopy);
                    }
                    Result result = reader.Decode(bitmap);
                    if (result != null)
                    {
                        Invoke(new Action(() =>
                        {
                            label1.Text = result.Text;
                        }));
                    }
                }
            }
        });
    }
```

参考

https://whoopsidaisies.hatenablog.com/entry/2013/09/25/192818

https://qiita.com/nekotadon/items/4e9c2608d758d13fc979