HoughLinesP()を使って直線検出をしてみます。
実行環境構築
プロジェクトの作成
mkdir プロジェクト名
cd プロジェクト名
dotnet new winforms
dotnet add package OpenCvSharp4.Windows
dotnet add package OpenCvSharp4.Extensions
code .
ソースプログラム
namespace HougLinesPSample;
using OpenCvSharp;
using OpenCvSharp.Extensions;
public partial class Form1 : Form
{
Mat? view = null;
public Form1()
{
InitializeComponent();
Text = "直線検出";
Size = new System.Drawing.Size(850, 800);
var fnt = new Font("MS UI Gothic", 12);
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 picboxV = new PictureBox {
SizeMode = PictureBoxSizeMode.Zoom,
Dock = DockStyle.Fill,
AllowDrop = true,
Image = vb,
};
var execBtn = new RadioButton {
Text = "フィルターOFF",
Appearance = Appearance.Button,
AutoCheck = false,
//Location = new System.Drawing.Point(10, 10),
Size = new System.Drawing.Size(180, 50),
};
var toClip = new Button {
Text = "コピー",
//Location = new System.Drawing.Point(200, 10),
Size = new System.Drawing.Size(120, 50),
};
var fromClip = new Button {
Text = "貼り付け",
//Location = new System.Drawing.Point(330, 10),
Size = new System.Drawing.Size(120, 50),
};
var threadholdTypesList = new string[] {"バイナリ", "バイナリ反転", "大津"};
var thresholdTypesCmb = new ComboBox {
Size = new System.Drawing.Size(100, 50),
DataSource = threadholdTypesList,
};
int threadholdValue = 127;
var threadholdValueLabel = new Label {
Text = "2値化閾値:" + threadholdValue,
};
var threadholdValueTrack = new TrackBar {
Minimum = 0,
Maximum = 255,
Value = threadholdValue,
};
threadholdValueTrack.ValueChanged += (sender, e) => {
threadholdValue = threadholdValueTrack.Value;
threadholdValueLabel.Text = "2値化閾値:" + threadholdValue;
};
int houghThreadholdValue = 80;
var houghThreadholdValueLabel = new Label {
Text = "ハフ閾値:" + houghThreadholdValue,
};
var houghThreadholdValueTrack = new TrackBar {
Minimum = 0,
Maximum = 255,
Value = houghThreadholdValue,
};
houghThreadholdValueTrack.ValueChanged += (sender, e) => {
houghThreadholdValue = houghThreadholdValueTrack.Value;
houghThreadholdValueLabel.Text = "ハフ閾値:" + houghThreadholdValue;
};
int minLineLengthValue = 80;
var minLineLengthValueLabel = new Label {
Text = "線長最小:" + minLineLengthValue,
};
var minLineLengthValueTrack = new TrackBar {
Minimum = 0,
Maximum = 255,
Value = minLineLengthValue,
};
minLineLengthValueTrack.ValueChanged += (sender, e) => {
minLineLengthValue = minLineLengthValueTrack.Value;
minLineLengthValueLabel.Text = "最小線長:" + minLineLengthValue;
};
int maxLineGapValue = 5;
var maxLineGapValueLabel = new Label {
Text = "最大ギャップ:" + maxLineGapValue,
};
var maxLineGapValueTrack = new TrackBar {
Minimum = 0,
Maximum = 255,
Value = maxLineGapValue,
};
maxLineGapValueTrack.ValueChanged += (sender, e) => {
maxLineGapValue = maxLineGapValueTrack.Value;
maxLineGapValueLabel.Text = "最大ギャップ:" + maxLineGapValue;
};
var flowPanel = new FlowLayoutPanel {
Dock = DockStyle.Fill,
};
picboxV.DragEnter += (sender, e) => {
if (e.Data == null) return;
if(!e.Data.GetDataPresent(DataFormats.FileDrop)) return;
e.Effect = DragDropEffects.Copy;
};
picboxV.DragDrop += (sender, e) => {
var ed = e.Data;
if (ed == null) return;
//if(!ed.GetDataPresent(DataFormats.FileDrop)) return;
var f = ed.GetData(DataFormats.FileDrop);
if(f == null) return;
string path = ((string[])f)[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.Checked = false;
};
execBtn.Click += async (sender, e) => {
if (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 {
int thresholdTypeIndex = thresholdTypesCmb.SelectedIndex;
await Task.Run(()=>{
using (var gray = new Mat())
using (var result = (Mat)view.Clone())
{
Cv2.CvtColor(result, gray, ColorConversionCodes.RGB2GRAY);
switch (thresholdTypeIndex) {
case 1:
Cv2.Threshold(gray, gray, threadholdValue, 255, ThresholdTypes.BinaryInv);
break;
case 2:
Cv2.Threshold(gray, gray, threadholdValue, 255, ThresholdTypes.Otsu);
break;
default:
Cv2.Threshold(gray, gray, threadholdValue, 255, ThresholdTypes.Binary);
break;
}
var lines = Cv2.HoughLinesP(gray, 1, Math.PI/360, houghThreadholdValue, (double)minLineLengthValue, (double)maxLineGapValue);
foreach(var line in lines) {
Cv2.Line(result, line.P1.X, line.P1.Y, line.P2.X, line.P2.Y, new Scalar(255, 0, 255), 3);
}
if (picboxV.Image is not null) picboxV.Image.Dispose();
picboxV.Image = BitmapConverter.ToBitmap(result);
}
});
execBtn.Text = "フィルターOn";
execBtn.Checked = true;
}
};
toClip.Click += (sender, e) => {
if (picboxV.Image is null) return;
var ms = new MemoryStream();
picboxV.Image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
Clipboard.SetData("PNG", ms);
};
fromClip.Click += (sender, e) => {
var obj = Clipboard.GetData("PNG");
if (obj == null) return;
var ms = (MemoryStream)obj;
if (ms is null) return;
if (view is not null) view.Dispose();
using(var b = new Bitmap(ms)) {
view = BitmapConverter.ToMat(b);
if (view.Channels() == 4)
Cv2.CvtColor(view, view, ColorConversionCodes.RGBA2RGB);
}
if (picboxV.Image is not null) picboxV.Image.Dispose();
picboxV.Image = BitmapConverter.ToBitmap(view);
execBtn.Checked = false;
};
ud.Panel1.Controls.Add(picboxV);
//ud.Panel2.Controls.AddRange(new Control[]{execBtn,toClip,fromClip});
flowPanel.Controls.AddRange(new Control[]{
execBtn,toClip,fromClip,thresholdTypesCmb,threadholdValueLabel,threadholdValueTrack,
houghThreadholdValueLabel, houghThreadholdValueTrack,
minLineLengthValueLabel, minLineLengthValueTrack,
maxLineGapValueLabel, maxLineGapValueTrack});
flowPanel.SetFlowBreak(fromClip, true);
flowPanel.SetFlowBreak(threadholdValueTrack, true);
ud.Panel2.Controls.Add(flowPanel);
Controls.Add(ud);
}
}
実行
dotnet run
枠が直線として認識されたためピンク色に変化しています。
コメント