OpenCVSharp「ピクセルの論理演算(AND,OR,XOR,NOT)」を試す。

コンピュータ
BitwiseAND,OR,XOR,NOTのサンプル

実行環境構築

プロジェクトの作成

mkdir プロジェクト名
cd プロジェクト名
dotnet new winforms
dotnet add package OpenCvSharp4.Windows -v 4.6.0.20220608
dotnet add package OpenCvSharp4.Extensions -v 4.6.0.20220608
code .

ソースプログラム

using OpenCvSharp;
using OpenCvSharp.Extensions;

namespace BitwiseAndSample;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        Text = "bitwise";

        // ベース画像
        var fnt = new Font("MS UI Gothic", 24);
        var resultBitmap = new Bitmap(800, 600);
        using (var g = Graphics.FromImage(resultBitmap))
        {
            g.FillRectangle(Brushes.White, 0, 0, resultBitmap.Width, resultBitmap.Height);
            g.DrawString("結果", fnt,  Brushes.Black, 0.0f, 0.0f);
        }
        var errBitmap = new Bitmap(800, 600);
        using (var g = Graphics.FromImage(errBitmap))
        {
            g.FillRectangle(Brushes.White, 0, 0, errBitmap.Width, errBitmap.Height);
            g.DrawString("画像のサイズが異なります。", fnt,  Brushes.Black, 0.0f, 0.0f);
        }

        var aBitmap = new Bitmap(800, 600);
        using (var g = Graphics.FromImage(aBitmap))
        {
            g.FillRectangle(Brushes.White, 0, 0, aBitmap.Width, aBitmap.Height);
            g.DrawString("A:画像をD&D", fnt,  Brushes.Black, 0.0f, 0.0f);
        }

        var bBitmap = new Bitmap(800, 600);
        using (var g = Graphics.FromImage(bBitmap))
        {
            g.FillRectangle(Brushes.White, 0, 0, bBitmap.Width, bBitmap.Height);
            g.DrawString("B:画像をD&D", fnt,  Brushes.Black, 0.0f, 0.0f);
        }
        // スプリットコンテナ
        var scViewConsole = new SplitContainer
        {
            Dock = DockStyle.Fill,
            Orientation = Orientation.Horizontal,   // 上下
            Panel1MinSize = 120,
        };
        // スプリットコンテナ
        var scInOut = new SplitContainer
        {
            Dock = DockStyle.Fill,
            Orientation = Orientation.Vertical,   // 左右
            Panel1MinSize = 240,
        };
        // スプリットコンテナ
        var scAb = new SplitContainer
        {
            Dock = DockStyle.Fill,
            Orientation = Orientation.Horizontal,   // 上下
            Panel1MinSize = 120,
        };
        // ピクチャボックス
        var picboxResult = new PictureBox
        {
            SizeMode = PictureBoxSizeMode.AutoSize,
            Image = (Bitmap)resultBitmap.Clone(),
        };
        // ピクチャボックス
        var picboxA = new PictureBox
        {
            SizeMode = PictureBoxSizeMode.AutoSize,
            Image = aBitmap,
        };
        // ピクチャボックス
        var picboxB = new PictureBox
        {
            SizeMode = PictureBoxSizeMode.AutoSize,
            Image = bBitmap,
        };
        // パネル
        var flowPanel = new FlowLayoutPanel
        {
            Dock = DockStyle.Fill,
        };
        // グレースケールチェックボックス
        var grayChkbox = new CheckBox
        {
            Checked = true,
            Text = "グレースケール",
        };
        // ANDボタン
        var andButton = new Button
        {
            Text = "AND",
            Size = new System.Drawing.Size(180, 50),
        };
        // ORボタン
        var orButton = new Button
        {
            Text = "OR",
            Size = new System.Drawing.Size(180, 50),
        };
        // XORボタン
        var xorButton = new Button
        {
            Text = "XOR",
            Size = new System.Drawing.Size(180, 50),
        };

        // NOT_Aボタン
        var notAButton = new Button
        {
            Text = "NOT_A",
            Size = new System.Drawing.Size(180, 50),
        };
        // NOT_Bボタン
        var notBButton = new Button
        {
            Text = "NOT_B",
            Size = new System.Drawing.Size(180, 50),
        };
        // コントロールの追加
        flowPanel.Controls.AddRange(new Control[]
        {
            grayChkbox,
            andButton,
            orButton,
            xorButton,
            notAButton,
            notBButton,
        });

        scAb.Panel1.Controls.Add(picboxA);
        scAb.Panel1.AutoScroll = true;
        scAb.Panel1.AllowDrop = true;

        scAb.Panel2.Controls.Add(picboxB);
        scAb.Panel2.AutoScroll = true;
        scAb.Panel2.AllowDrop = true;

        scInOut.Panel1.Controls.Add(picboxResult);
        scInOut.Panel1.AutoScroll = true;
        scInOut.Panel1.AllowDrop = false;

        scInOut.Panel2.Controls.Add(scAb);
        scViewConsole.Panel1.Controls.Add(scInOut);
        scViewConsole.Panel2.Controls.Add(flowPanel);

        Controls.Add(scViewConsole);

        // イベント

        // ドラッグエンターイベント
        scAb.Panel1.DragEnter += (sender, e) =>
        {
            if (e.Data == null) return;
            if(!e.Data.GetDataPresent(DataFormats.FileDrop)) return;
            e.Effect = DragDropEffects.Copy;
        };
        // ドラッグアンドドロップイベント
        scAb.Panel1.DragDrop += (sender, e) =>
        {
            if (e.Data == null) return;
            var fd = e.Data.GetData(DataFormats.FileDrop);
            if (fd == null) return;
            string? path = ((string[])fd)[0];
            picboxA.Image?.Dispose();
            using(var fs = new FileStream(path, FileMode.Open))
            {
                picboxA.Image = new Bitmap(fs);
            }
        };
        // ドラッグエンターイベント
        scAb.Panel2.DragEnter += (sender, e) =>
        {
            if (e.Data == null) return;
            if(!e.Data.GetDataPresent(DataFormats.FileDrop)) return;
            e.Effect = DragDropEffects.Copy;
        };
        // ドラッグアンドドロップイベント
        scAb.Panel2.DragDrop += (sender, e) =>
        {
            if (e.Data == null) return;
            var fd = e.Data.GetData(DataFormats.FileDrop);
            if (fd == null) return;
            string? path = ((string[])fd)[0];
            picboxB.Image?.Dispose();
            using(var fs = new FileStream(path, FileMode.Open))
            {
                picboxB.Image = new Bitmap(fs);
            }
        };
        // ANDボタンをクリック
        andButton.Click += (s, e) =>
        {
            if (picboxA.Image.Width != picboxB.Image.Width || picboxA.Image.Height != picboxB.Image.Height)
            {
                picboxResult.Image?.Dispose();
                picboxResult.Image = (Bitmap)errBitmap.Clone();
                return;
            }
            using var a = BitmapConverter.ToMat((Bitmap)picboxA.Image);
            using var b = BitmapConverter.ToMat((Bitmap)picboxB.Image);
            using var result = new Mat();

            if (grayChkbox.Checked && a.Channels() > 1)
                Cv2.CvtColor(a, a, ColorConversionCodes.RGB2GRAY);
            if (grayChkbox.Checked && b.Channels() > 1)
                Cv2.CvtColor(b, b, ColorConversionCodes.RGB2GRAY);
            
            Cv2.BitwiseAnd(a, b, result);
            picboxResult.Image?.Dispose();
            picboxResult.Image = BitmapConverter.ToBitmap(result);
        };
        // ORボタンをクリック
        orButton.Click += (s, e) =>
        {
            if (picboxA.Image.Width != picboxB.Image.Width || picboxA.Image.Height != picboxB.Image.Height)
            {
                picboxResult.Image?.Dispose();
                picboxResult.Image = (Bitmap)errBitmap.Clone();
                return;
            }
            using var a = BitmapConverter.ToMat((Bitmap)picboxA.Image);
            using var b = BitmapConverter.ToMat((Bitmap)picboxB.Image);
            using var result = new Mat();
            
            
            if (grayChkbox.Checked && a.Channels() > 1)
                Cv2.CvtColor(a, a, ColorConversionCodes.RGB2GRAY);
            if (grayChkbox.Checked && b.Channels() > 1)
                Cv2.CvtColor(b, b, ColorConversionCodes.RGB2GRAY);

            Cv2.BitwiseOr(a, b, result);
            picboxResult.Image?.Dispose();
            picboxResult.Image = BitmapConverter.ToBitmap(result);
        };
        // XORボタンをクリック
        xorButton.Click += (s, e) =>
        {
            if (picboxA.Image.Width != picboxB.Image.Width || picboxA.Image.Height != picboxB.Image.Height)
            {
                picboxResult.Image?.Dispose();
                picboxResult.Image = (Bitmap)errBitmap.Clone();
                return;
            }
            using var a = BitmapConverter.ToMat((Bitmap)picboxA.Image);
            using var b = BitmapConverter.ToMat((Bitmap)picboxB.Image);
            using var result = new Mat();

            if (grayChkbox.Checked && a.Channels() > 1)
                Cv2.CvtColor(a, a, ColorConversionCodes.RGB2GRAY);
            if (grayChkbox.Checked && b.Channels() > 1)
                Cv2.CvtColor(b, b, ColorConversionCodes.RGB2GRAY);

            Cv2.BitwiseXor(a, b, result);
            picboxResult.Image?.Dispose();
            picboxResult.Image = BitmapConverter.ToBitmap(result);
        };
        // NOT_Aボタンをクリック
        notAButton.Click += (s, e) =>
        {
            using var a = BitmapConverter.ToMat((Bitmap)picboxA.Image);
            using var result = new Mat();

            if (grayChkbox.Checked && a.Channels() > 1)
                Cv2.CvtColor(a, a, ColorConversionCodes.RGB2GRAY);

            Cv2.BitwiseNot(a, result);
            picboxResult.Image?.Dispose();
            picboxResult.Image = BitmapConverter.ToBitmap(result);
        };
        // NOT_Bボタンをクリック
        notBButton.Click += (s, e) =>
        {
            using var b = BitmapConverter.ToMat((Bitmap)picboxB.Image);
            using var result = new Mat();
            
            if (grayChkbox.Checked && b.Channels() > 1)
                Cv2.CvtColor(b, b, ColorConversionCodes.RGB2GRAY);

            Cv2.BitwiseNot(b, result);
            picboxResult.Image?.Dispose();
            picboxResult.Image = BitmapConverter.ToBitmap(result);
        };
    }
}

実行

dotnet run

使い方

A:及びB:に画像をドラッグアンドドロップ。

高さ及び幅が異なる画像は不可。カラー画像はグレースケールに変換されます。

AND,OR,XOR,NOT_A,NOT_Bの各ボタンを押すと論理演算された結果が表示されます。

問題点

カラー画像とグレースケール画像で処理を実行するとエラーになります。
RGBの各チャンネル毎に処理する必要があると思われます。

コメント