画像の一致部分を緑枠で囲むプログラムです。
実行環境構築
プロジェクトの作成
mkdir プロジェクト名
cd プロジェクト名
dotnet new winforms
dotnet add package OpenCvSharp4.Windows
dotnet add package OpenCvSharp4.Extensions
code .
ソースプログラム
namespace TemplateMatchingSample;
using OpenCvSharp;
using OpenCvSharp.Extensions;
public partial class Form1 : Form
{
Mat? templ = null;
Mat? view = null;
public Form1()
{
InitializeComponent();
Text = "テンプレートマッチング";
Size = new System.Drawing.Size(850, 800);
var fnt = new Font("MS UI Gothic", 12);
var tb = new Bitmap(100, 100);
using (var g = Graphics.FromImage(tb)) {
g.FillRectangle(Brushes.White, 0, 0, tb.Width, tb.Height);
g.DrawString("こちらにD&D", fnt, Brushes.Blue, 0.0f, 0.0f);
}
var vb = new Bitmap(800, 600);
using (var g = Graphics.FromImage(vb)) {
g.FillRectangle(Brushes.White, 0, 0, vb.Width, vb.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 picboxT = new PictureBox {
Location = new System.Drawing.Point(10, 10),
Size = new System.Drawing.Size(tb.Width, tb.Height),
AllowDrop = true,
Image = tb,
};
var picboxV = new PictureBox {
Location = new System.Drawing.Point(10, 10),
Size = new System.Drawing.Size(vb.Width, vb.Height),
AllowDrop = true,
Image = vb,
};
var execBtn = new RadioButton {
Text = "フィルターOFF",
Appearance = Appearance.Button,
AutoCheck = false,
Location = new System.Drawing.Point(120, 10),
Size = new System.Drawing.Size(180, 50),
};
picboxT.DragEnter += (sender, e) => {
if (e.Data == null) return;
if(!e.Data.GetDataPresent(DataFormats.FileDrop)) return;
e.Effect = DragDropEffects.Copy;
};
picboxT.DragDrop += (sender, e) => {
if (e.Data == null) return;
if(!e.Data.GetDataPresent(DataFormats.FileDrop)) return;
string path = ((string[])e.Data.GetData(DataFormats.FileDrop))[0];
if (templ is not null) templ.Dispose();
templ = Cv2.ImRead(path);
if (picboxT.Image is not null) picboxT.Image.Dispose();
picboxT.Image = BitmapConverter.ToBitmap(templ);
};
picboxV.DragEnter += (sender, e) => {
if (e.Data == null) return;
if(!e.Data.GetDataPresent(DataFormats.FileDrop)) return;
e.Effect = DragDropEffects.Copy;
};
picboxV.DragDrop += (sender, e) => {
if (e.Data == null) return;
if(!e.Data.GetDataPresent(DataFormats.FileDrop)) return;
string path = ((string[])e.Data.GetData(DataFormats.FileDrop))[0];
if (view is not null) view.Dispose();
view = Cv2.ImRead(path);
if (picboxV.Image is not null) picboxV.Image.Dispose();
picboxV.Image = BitmapConverter.ToBitmap(view);
};
execBtn.Click += (sender, e) => {
if (templ is null || view is null) return;
if (execBtn.Checked == true) {
if (picboxV.Image is not null) picboxV.Image.Dispose();
picboxV.Image = BitmapConverter.ToBitmap(view);
execBtn.Text = "フィルターOff";
execBtn.Checked = false;
} else {
var view_gray = new Mat();
var templ_gray = new Mat();
var result = new Mat();
Cv2.CvtColor(view, view_gray, ColorConversionCodes.RGB2GRAY);
Cv2.CvtColor(templ, templ_gray, ColorConversionCodes.RGB2GRAY);
var result_view = (Mat)view.Clone();
var result_tmp = (Mat)view_gray.Clone();
while (true) {
Cv2.MatchTemplate(result_tmp, templ_gray, result, TemplateMatchModes.CCoeffNormed);
double minval, maxval, threshold = 0.8;
Point minloc, maxloc;
Cv2.MinMaxLoc(result, out minval, out maxval, out minloc, out maxloc);
if (maxval >= threshold) {
Cv2.Rectangle(result_view, new Rect(maxloc.X, maxloc.Y, templ.Width, templ.Height), new Scalar(0, 255, 0), 3);
Cv2.Rectangle(result_tmp, new Rect(maxloc.X, maxloc.Y, templ.Width, templ.Height), new Scalar(0, 0, 255), 3);
}
else
break;
}
if (picboxV.Image is not null) picboxV.Image.Dispose();
picboxV.Image = BitmapConverter.ToBitmap(result_view);
execBtn.Text = "フィルターOn";
execBtn.Checked = true;
}
};
ud.Panel1.Controls.Add(picboxV);
ud.Panel2.Controls.AddRange(new Control[] {picboxT, execBtn});
Controls.Add(ud);
}
}
実行
dotnet run
対象画像
テンプレート画像(対象画像からロゴを切り抜き)
フォームを起動
対象画像を上部にテンプレート画像を下部にドラッグアンドドロップ
フィルタボタンを押す。
MatchTemplate()の結果にマッチした位置情報が複数セットされているようですが、筆者には個別に取得する方法を見つけることが出来ませんでした。結果からMinMaxLoc()で最大の位置情報が取得できるようなので、その位置情報から枠線を描画し、枠線を描画した画像にMatchTemplate()を実行、マッチしなくなるまで繰り返しています。
何度もMatchTemplate()をするので効率も悪いですし、オリジナル画像に枠線を加筆するので、マッチングにも影響がありそうです。(テンプレートによっては枠線を描画した部分にもマッチングする可能性も)
コメント