OpenCvSharp「指定サイズより小さい図形を塗りつぶす」

コンピュータ

実行環境構築

プロジェクトの作成

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 EraseShape;
public partial class Form1 : Form
{
    Mat? _view = null;

    public Form1()
    {
        InitializeComponent();

        Text = "指定サイズより小さい図形を塗潰す";
        Size = new System.Drawing.Size(800, 600);

        // ベース画像
        var fnt = new Font("MS UI Gothic", 24);
        var bmp = new Bitmap(800, 600);
        using (var g = Graphics.FromImage(bmp))
        {
            g.FillRectangle(Brushes.White, 0, 0, bmp.Width, bmp.Height);
            g.DrawString("こちらにD&D", fnt,  Brushes.Green, 0.0f, 0.0f);
        }
        // スプリットコンテナ(上下)
        var ud = new SplitContainer
        {
            Dock = DockStyle.Fill,
            Orientation = Orientation.Horizontal,   // 上下
            Panel1MinSize = 120,
        };
        // ピクチャボックス
        var picbox = new PictureBox
        {
            SizeMode = PictureBoxSizeMode.Zoom,
            Dock = DockStyle.Fill,
            AllowDrop = true,
            Image = bmp,
        };
        // 実行ボタン
        var execBtn = new RadioButton
        {
            Text = "フィルターOFF",
            Appearance = Appearance.Button,
            AutoCheck = false,
            Size = new System.Drawing.Size(180, 50),
        };
        // ドットサイズ
        var dotSizeLabel = new Label
        {
            Text = "ドットサイズ:50"
        };
        var dotSizeTrac = new TrackBar
        {
            Minimum = 1,
            Maximum = 500,
            Value = 50,
            LargeChange = 50,
        };
        // パネル
        var flowPanel = new FlowLayoutPanel
        {
            Dock = DockStyle.Fill,
        };
        // ドラッグエンターイベント
        picbox.DragEnter += (sender, e) =>
        {
            if (e.Data == null) return;
            if(!e.Data.GetDataPresent(DataFormats.FileDrop)) return;
            e.Effect = DragDropEffects.Copy;
        };
        // ドラッグアンドドロップイベント
        picbox.DragDrop += (sender, e) =>
        {
            if (e.Data == null) return;
            var fd = e.Data.GetData(DataFormats.FileDrop);
            if (fd == null) return;
            string? path = ((string[])fd)[0];
            _view?.Dispose();
            _view = Cv2.ImRead(path);
            picbox.Image?.Dispose();
            picbox.Image = BitmapConverter.ToBitmap(_view);
            execBtn.Checked = false;
        };
        // ボタンクリックイベント
        execBtn.Click += (sender, e) =>
        {
            if (_view is null) return;
            picbox.Image?.Dispose();
            if (execBtn.Checked == true)
            {
                picbox.Image = BitmapConverter.ToBitmap(_view);
                execBtn.Text = "フィルターOff";
                execBtn.Checked = false;
            } else {
                using (var img = (Mat)_view.Clone()) 
                using (var dst = (Mat)_view.Clone()) 
                {
                    int dotSize = dotSizeTrac.Value;
                    if (_view.Channels() > 1)
                    {
                        // RGB=>グレースケール
                        Cv2.CvtColor(_view, img, ColorConversionCodes.RGB2GRAY);
                    }
                    // 2値化
                    Cv2.Threshold(img, img, 127, 255, ThresholdTypes.Binary);

                    // 図形の検出
                    OpenCvSharp.Point[][] contours;
                    OpenCvSharp.HierarchyIndex[] hierarchy;
                    Cv2.FindContours(img, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxNone);
                    //Cv2.FindContours(img, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);

                    List<List<OpenCvSharp.Point>> targets = new();

                    foreach(var c in contours)
                    {
                        if (Math.Abs(Cv2.ContourArea(c)) <= dotSize)
                        {
                            var lc = new List<OpenCvSharp.Point>(c);
                            targets.Add(lc);
                        }
                    }

                    // 検出図形を塗りつぶし
                    Cv2.DrawContours(dst, targets, -1, new Scalar(255,0,255), -1);

                    picbox.Image?.Dispose();
                    picbox.Image = BitmapConverter.ToBitmap(dst);
                }

                execBtn.Text = "フィルターOn";
                execBtn.Checked = true;
            }
        };
        // ドットサイズ変更イベント
        dotSizeTrac.ValueChanged += (s, e) =>
        {
            dotSizeLabel.Text = string.Format("ドットサイズ:{0}", dotSizeTrac.Value);
        };
        // コントロールの追加
        ud.Panel1.Controls.Add(picbox);
        flowPanel.Controls.AddRange(new Control[]
        {
            execBtn,
            dotSizeLabel,
            dotSizeTrac,
        });
        ud.Panel2.Controls.Add(flowPanel);
        Controls.Add(ud);
    }
}

 

実行

dotnet run

フィルターOFF

ドットサイズ:5

ドットサイズ:250

ドットサイズ:500

GIMPのスクリーンショットを対象にしてみたところ、アイコンやメニューの文字が検出されています。ドットサイズを大きくすると塗りつぶされる対象が拡大されています。
図形検出の為、画像を2値化処理で白黒にしているのですが、こちらの閾値や処理方法によっても結果が異なると思われます。

コメント