using OpenCvSharp;
using OpenCvSharp.Extensions;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using Point = OpenCvSharp.Point;
using Size = OpenCvSharp.Size;
namespace DrawROI
{
public partial class Form1 : Form
{
private System.Drawing.Point RectStartPoint, tempEndPoint;
bool blnDraw;
Mat ImageROI;
Mat OrgMat;
private string FilePath;
public Form1()
{
InitializeComponent();
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
RectStartPoint = e.Location; //获得鼠标按下的pictureBox上坐标
Invalidate();
blnDraw = true;//判断标志
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (blnDraw)
{
if (e.Button != MouseButtons.Left)//判断是否按下左键
{
return;
}
tempEndPoint = e.Location; //记录框的位置和大小
//pictureBox上开始点坐标
//Rect.Location = new System.Drawing.Point(
//Math.Min(RectStartPoint.X, tempEndPoint.X),
//Math.Min(RectStartPoint.Y, tempEndPoint.Y));
pictureBox上矩形大小
//Rect.Size = new System.Drawing.Size(
//Math.Abs(RectStartPoint.X - tempEndPoint.X),
//Math.Abs(RectStartPoint.Y - tempEndPoint.Y));
pictureBox1.Invalidate();
// 最后点位置
int X0, Y0;
Utilities.ConvertCoordinates(pictureBox1, out X0, out Y0, e.X, e.Y);
//在控件中
//textBox1.Text = Convert.ToString("pictureBox最后点坐标" + e.X + " ," + e.Y); //pictureBox 上终点坐标
//textBox2.Text = Convert.ToString("pictureBox开始点坐标" + Rect.X + " ," + Rect.Y); //开始点坐标
//textBox3.Text = Convert.ToString("pictureBox的Width" + Rect.Width + " ," + Rect.Height);//大小
//Create ROI 感兴趣区域
Utilities.ConvertCoordinates(pictureBox1, out X0, out Y0, RectStartPoint.X, RectStartPoint.Y);
int X1, Y1;
Utilities.ConvertCoordinates(pictureBox1, out X1, out Y1, tempEndPoint.X, tempEndPoint.Y);
//感兴趣区域 左上点坐标-宽-高
//RealImageRect.Location = new System.Drawing.Point(
// Math.Min(X0, X1),
// Math.Min(Y0, Y1));
//RealImageRect.Size = new System.Drawing.Size(
// Math.Abs(X0 - X1),
// Math.Abs(Y0 - Y1));
//textBox4.Text = "原图像上最后点坐标: X:" + X0 + " Y:" + Y0;
//textBox5.Text = "原图像上RealImageRect: X:" + RealImageRect.X + " Y:" + RealImageRect.Y; // 原图像-左上点坐标
//textBox6.Text = "原图像上RealImageRectSize: X:" + RealImageRect.Width + " Y:" + RealImageRect.Height; // 原图像-大小
Rect tmp_Rect = new Rect(Math.Min(X0, X1), Math.Min(Y0, Y1), Math.Abs(X0 - X1), Math.Abs(Y0 - Y1));
ImageROI = new Mat(OrgMat, tmp_Rect);//新建一个mat,把roi内的图像加载到里面去。
//Cv2.ImWrite("4.jpg",ImageROI); //保存
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
// mouseUp 结束以后 将图像显示在pictureBox2控件中
pictureBox2.Image = ImageROI.ToBitmap();
//***************************************//
blnDraw = false; //结束绘制
}
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.ShowDialog();
//openFileDialog.Multiselect=true;
FilePath = openFileDialog.FileName;
OrgMat = new Mat(FilePath, ImreadModes.Grayscale);
OrgMat.MedianBlur(3);
pictureBox1.Image = BitmapConverter.ToBitmap(OrgMat);
}
private void button3_Click(object sender, EventArgs e)
{
if (ImageROI == null)
{
MessageBox.Show("请先绘制模板");
return ;
}
Mat RoiClone = ImageROI.Clone();
Mat mat3 = new Mat();
//创建result的模板,就是MatchTemplate里的第三个参数
//mat3.Create(mat1.Cols - mat2.Cols + 1, mat1.Rows - mat2.Rows + 1, MatType.CV_32FC1);
//进行匹配(1母图,2模版子图,3返回的result,4匹配模式)
Cv2.MatchTemplate(OrgMat, RoiClone, mat3, TemplateMatchModes.SqDiff);
//对结果进行归一化(这里我测试的时候没有发现有什么用,但在opencv的书里有这个操作,应该有什么神秘加成,这里也加上)
Cv2.Normalize(mat3, mat3, 1, 0, NormTypes.MinMax, -1);
//double minValue, maxValue;
Point minLocation, maxLocation;
/// 通过函数 minMaxLoc 定位最匹配的位置
/// (这个方法在opencv里有5个参数,这里我写的时候发现在有3个重载,看了下可以直接写成拿到起始坐标就不取最大值和最小值了)
/// minLocation和maxLocation根据匹配调用的模式取不同的点
Cv2.MinMaxLoc(mat3, out minLocation, out maxLocation);
Mat OrgMatClone = OrgMat.Clone();
//画出匹配的矩,
//Cv2.Rectangle(mask, maxLocation, new Point(maxLocation.X + mat2.Cols, maxLocation.Y + mat2.Rows), Scalar.Red, 2);
//Cv2.Rectangle(OrgMatClone, minLocation, new Point(minLocation.X + RoiClone.Cols, minLocation.Y + RoiClone.Rows), Scalar.Red, 2);
//Cv2.ImShow("mat1", mat1);
//Cv2.ImShow("mat2", mat2);
//霍夫圆检测:使用霍夫变换查找灰度图像中的圆。
/*
* 参数:
* 1:输入参数: 8位、单通道、灰度输入图像
* 2:实现方法:目前,唯一的实现方法是HoughCirclesMethod.Gradient
* 3: dp :累加器分辨率与图像分辨率的反比。默认=1
* 4:minDist: 检测到的圆的中心之间的最小距离。(最短距离-可以分辨是两个圆的,否则认为是同心圆- src_gray.rows/8)
* 5:param1: 第一个方法特定的参数。[默认值是100] canny边缘检测阈值低
* 6:param2: 第二个方法特定于参数。[默认值是100] 中心点累加器阈值 – 候选圆心
* 7:minRadius: 最小半径
* 8:maxRadius: 最大半径
*
*/
CircleSegment[] cs = Cv2.HoughCircles(RoiClone, HoughMethods.Gradient, 1, 80, 70, 100, 100, 200);
for (int i = 0; i < cs.Count(); i++)
{
//画圆
Cv2.Circle(OrgMatClone, (int)(cs[i].Center.X + minLocation.X), (int)(cs[i].Center.Y + minLocation.Y), (int)cs[i].Radius, new Scalar(0, 0, 255), 2, LineTypes.AntiAlias);
//加强圆心显示
Cv2.Circle(OrgMatClone, (int)cs[i].Center.X, (int)cs[i].Center.Y, 3, new Scalar(0, 0, 255), 2, LineTypes.AntiAlias);
textBox1.Text = cs[i].Center.X.ToString();
textBox2.Text = cs[i].Center.Y.ToString();
}
//pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox1.Image = BitmapConverter.ToBitmap(OrgMatClone);
}
//private void pictureBox1_Paint(object sender, PaintEventArgs e)
//{
// if (blnDraw)
// {
// if (pictureBox1.Image != null)
// {
// if (Rect != null && Rect.Width > 0 && Rect.Height > 0)
// {
// e.Graphics.DrawRectangle(new Pen(Color.Red, 1), Rect);//重新绘制颜色为红色
// }
// }
// }
//}
public class Utilities
{
//坐标转换
//**************************************
//* 图片左边转换,
//* Input输入: pictureBox 坐标X,Y
//* Output输出: Image 图像上对应的坐标
//**************************************//
public static void ConvertCoordinates(PictureBox pic,
out int X0, out int Y0, int x, int y)
{
int pic_hgt = pic.ClientSize.Height;
int pic_wid = pic.ClientSize.Width;
int img_hgt = pic.Image.Height;
int img_wid = pic.Image.Width;
X0 = x;
Y0 = y;
switch (pic.SizeMode)
{
case PictureBoxSizeMode.AutoSize:
case PictureBoxSizeMode.StretchImage:
X0 = (int)(img_wid * x / (float)pic_wid);
Y0 = (int)(img_hgt * y / (float)pic_hgt);
break;
}
}
}
//******************************************************************//
///
/// 多角度模板匹配方法
///
/// 待匹配图像
/// 模板图像
/// 起始角度
/// 角度范围
/// 角度步长
/// 金字塔层级
/// 得分阈值
///
//Mat ImageRotate;
//private ResultPoint CircleMatchNcc(Mat srcImage, Mat modelImage, double angleStart, double angleRange, double angleStep, int numLevels, double thresScore, int nccMethod)
//{
// double step = angleRange / ((angleRange / angleStep) / 100);
// double start = angleStart;
// double range = angleRange;
// //定义图片匹配所需要的参数
// int resultCols = srcImage.Cols - modelImage.Cols + 1;
// int resultRows = srcImage.Rows - modelImage.Cols + 1;
// Mat result = new Mat(resultCols, resultRows, MatType.CV_8U);
// Mat src = new Mat();
// Mat model = new Mat();
// srcImage.CopyTo(src);
// modelImage.CopyTo(model);
// //对模板图像和待检测图像分别进行图像金字塔下采样
// for (int i = 0; i < numLevels; i++)
// {
// Cv2.PyrDown(src, src, new Size(src.Cols / 2, src.Rows / 2));
// Cv2.PyrDown(model, model, new Size(model.Cols / 2, model.Rows / 2));
// }
// TemplateMatchModes matchMode = TemplateMatchModes.CCoeffNormed;
// switch (nccMethod)
// {
// case 0:
// matchMode = TemplateMatchModes.SqDiff;
// break;
// case 1:
// matchMode = TemplateMatchModes.SqDiffNormed;
// break;
// case 2:
// matchMode = TemplateMatchModes.CCorr;
// break;
// case 3:
// matchMode = TemplateMatchModes.CCorrNormed;
// break;
// case 4:
// matchMode = TemplateMatchModes.CCoeff;
// break;
// case 5:
// matchMode = TemplateMatchModes.CCoeffNormed;
// break;
// }
// //在没有旋转的情况下进行第一次匹配
// Cv2.MatchTemplate(src, model, result, matchMode);
// Cv2.MinMaxLoc(result, out double minVal, out double maxVal, out Point minLoc, out Point maxLoc, new Mat());
// Point location = maxLoc;
// double temp = maxVal;
// double angle = 0;
// Mat newImg;
// //以最佳匹配点左右十倍角度步长进行循环匹配,直到角度步长小于参数角度步长
// if (nccMethod == 0 || nccMethod == 1)
// {
// do
// {
// for (int i = 0; i <= (int)range / step; i++)
// {
// newImg = ImageRotate(model, start + step * i);
// Cv2.MatchTemplate(src, newImg, result, matchMode);
// Cv2.MinMaxLoc(result, out double minval, out double maxval, out Point minloc, out Point maxloc, new Mat());
// if (maxval < temp)
// {
// location = maxloc;
// temp = maxval;
// angle = start + step * i;
// }
// }
// range = step * 2;
// start = angle - step;
// step = step / 10;
// } while (step > angleStep);
// return new ResultPoint(location.X * Math.Pow(2, numLevels) + modelImage.Width / 2, location.Y * Math.Pow(2, numLevels) + modelImage.Height / 2, -angle, temp);
// }
// else
// {
// do
// {
// for (int i = 0; i <= (int)range / step; i++)
// {
// newImg = ImageRotate(model, start + step * i);
// Cv2.MatchTemplate(src, newImg, result, matchMode);
// Cv2.MinMaxLoc(result, out double minval, out double maxval, out Point minloc, out Point maxloc, new Mat());
// if (maxval > temp)
// {
// location = maxloc;
// temp = maxval;
// angle = start + step * i;
// }
// }
// range = step * 2;
// start = angle - step;
// step = step / 10;
// } while (step > angleStep);
// if (temp > thresScore)
// {
// return new ResultPoint(location.X * Math.Pow(2, numLevels), location.Y * Math.Pow(2, numLevels), -angle, temp);
// }
// }
// return new ResultPoint();
//}
旋转模板
//private Mat ImageRotate(Mat src, double angle)
//{
// Mat dst = new Mat();
// Point2f center = new Point2f(src.Cols / 2, src.Rows / 2);
// Mat rot = Cv2.GetRotationMatrix2D(center, angle, 1);
// Size2f s2f = new Size2f(src.Size().Width, src.Size().Height);
// Rect box = new RotatedRect(new Point2f(0, 0), s2f, (float)angle).BoundingRect();
// double xx = rot.At(0, 2) + box.Width / 2 - src.Cols / 2;
// double zz = rot.At(1, 2) + box.Height / 2 - src.Rows / 2;
// rot.Set(0, 2, xx);
// rot.Set(1, 2, zz);
// Cv2.WarpAffine(src, dst, rot, box.Size);
// return dst;
}
}