这个例子我在winform中写的,做了一些二值化、高斯滤波处理,然后用霍夫圆检测或者网格提取法,代码如下:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private Mat imgscr = null;
private void Form1_Load(object sender, EventArgs e)
{
LoadPicture();
// 相机标定
//var objectPoints = ExternalParameter.GetObjectPoints();
//var imagePoints = ExternalParameter.GetImagePoints(out int pw, out int ph);
using Mat cameraMatrix = new Mat(3, 3, DepthType.Cv32F, 0);
using Mat distortionCoeffs = new Mat(1, 5, DepthType.Cv32F, 0);
//MCvTermCriteria termCriteria = new MCvTermCriteria(10);
//using Matrix<double> matrix = ExternalParameter.GetCameraInternalParameterMatrix();
//using Matrix<double> matrix2 = ExternalParameter.GetDistortionMatrix(ks: new double[] { 1, 1, 1, 1 });
//var result = CvInvoke.CalibrateCamera(objectPoints, imagePoints, new Size(pw, ph), matrix, matrix2, CalibType.UserIntrinsicGuess, termCriteria, out Mat[] rotationVectors, out Mat[] translationVectors);
//var fisrt = rotationVectors.First().GetInputArray();
//richTextBox1.Text = string.Join(',', fisrt);
//rotationVectors = null;
//translationVectors = null;
//Mat mat = new Mat();
二值化
//CvInvoke.Threshold(imgscr, mat, 163, 255, ThresholdType.Binary);
// 高斯滤波去噪
//参数:输入图像,输出图像,内核,高斯核在x方向的标准差,高斯核在y方向的标准差
//sigmaY=0时,其值自动由sigmaX确定(sigmaY=sigmaX);
//sigmaY=sigmaX=0时,它们的值将由ksize.width和ksize.height自动确定))
//CvInvoke.GaussianBlur(mat, mat, new Size(5, 5), 0);
//this.pictureBox2.Image = mat.Bitmap;
//bmp.Dispose();
//CvInvoke.Imshow("img", mat);//显示图像
//CvInvoke.WaitKey(0);//按键等待 findChessboardCorners
//Mat mat = new Mat();
//var status= CvInvoke.FindChessboardCorners(imgscr,new Size(4,6), mat);
//OutputArray data = mat.GetOutputArray();
//data.Dispose();
}
private void LoadPicture()
{
if (imgPath != string.Empty)
{
imgscr = CvInvoke.Imread(imgPath, LoadImageType.Grayscale);
//CvInvoke.CornerSubPix(imgscr, imgscr,new Size(imgscr.Width>>1, imgscr.Bitmap.Height>>1),new Size(-1,-1)):
//CvInvoke.CalibrateCamera
this.pictureBox1.Image = imgscr.Bitmap;
}
}
/// <summary>
/// 二值化阈值
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void trackBar1_Scroll(object sender, EventArgs e)
{
if (checkBox1.Checked && imgscr != null)
{
label3.Text = trackBar1.Value.ToString();
// 二值化
CvInvoke.Threshold(imgscr, imgscr, trackBar1.Value, trackBar2.Value, ThresholdType.Binary);
this.pictureBox1.Image = imgscr.Bitmap;
}
}
/// <summary>
/// 二值化maxValue
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void trackBar2_Scroll(object sender, EventArgs e)
{
if (checkBox1.Checked && imgscr != null)
{
label4.Text = trackBar2.Value.ToString();
// 二值化
CvInvoke.Threshold(imgscr, imgscr, trackBar1.Value, trackBar2.Value, ThresholdType.Binary);
this.pictureBox1.Image = imgscr.Bitmap;
}
/* double threshold(InputArray src,//src输入数组(多通道,8位或32位浮点)。
OutputArray dst,//dst输出数组的大小和类型与src相同,通道数相同。
double thresh, //二值化阈值,阈值两端的分别置为0、255
double maxval, //与#THRESH_BINARY=0和#THRESH_BINARY_INV=1一起使用的maxval最大值类型。(8位图像为255)
int type //二值化类型
);
CV_THRESH_BINARY = 0, /**值大于阈值置为最大值,否则为0
CV_THRESH_BINARY_INV = 1, /**值大于阈值置为0,否则为最大值
CV_THRESH_TRUNC = 2, /**值大于阈值置为阈值,否则不变
CV_THRESH_TOZERO = 3, /**值大于阈值不变,否则置为0
CV_THRESH_TOZERO_INV = 4, /**值大于阈值置为0,否则不变
CV_THRESH_OTSU = 8, /**<利用大津法自动选择最优阈值;将标志与上述CV_THRESH_x值之一组合
CV_THRESH_TRIANGLE = 16 /**采用三角形算法选择最优阈值;将标志与上述CV_THRESH_x值之一组合,但不能用CV_THRESH_OTSU
*/
}
private void Disposable()
{
imgscr?.Dispose();
}
~Form1()
{
Disposable();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Disposable();
this.Dispose();
Application.ExitThread();
Process.GetCurrentProcess().Kill();
}
/// <summary>
/// 高斯核大小width
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void trackBar4_Scroll(object sender, EventArgs e)
{
if (checkBox2.Checked && imgscr != null && (trackBar4.Value & 1) == 1)
{
label8.Text = trackBar4.Value.ToString();
CvInvoke.GaussianBlur(imgscr, imgscr, new Size(trackBar4.Value, trackBar3.Value), (double)numericUpDown1.Value, (double)numericUpDown2.Value);
this.pictureBox1.Image = imgscr.Bitmap;
}
}
/// <summary>
/// 高斯核大小height
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void trackBar3_Scroll(object sender, EventArgs e)
{
if (checkBox2.Checked && imgscr != null && (trackBar3.Value & 1) == 1)
{
label7.Text = trackBar3.Value.ToString();
CvInvoke.GaussianBlur(imgscr, imgscr, new Size(trackBar4.Value, trackBar3.Value), (double)numericUpDown1.Value, (double)numericUpDown2.Value);
this.pictureBox1.Image = imgscr.Bitmap;
}
/*
Size ksize, (高斯核大小,ksize.width和ksize.height可以不同,但是都必须为正的奇数(或者为0,此时它们的值会自动由sigma进行计算))
double sigmaX, (高斯核在x方向的标准差)
double sigmaY=0, (高斯核在y方向的标准差(sigmaY=0时,其值自动由sigmaX确定(sigmaY=sigmaX);sigmaY=sigmaX=0时,它们的值将由ksize.width和ksize.height自动确定))
int borderType=BORDER_DEFAULT (像素外插策略,可参考BorderTypes)
*/
}
/// <summary>
/// 启动霍夫圆检测
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
StartHoughCircles();
}
private void StartHoughCircles()
{
if (imgscr != null)
{
// 霍夫圆检测
CircleF[] circles = CvInvoke.HoughCircles(imgscr, HoughType.Gradient, (double)numericUpDown3.Value, (double)numericUpDown4.Value, (double)numericUpDown5.Value, (double)numericUpDown6.Value, trackBar6.Value, trackBar5.Value);
if (circles == null)
return;
Bitmap bmp = new Bitmap(imgscr.Bitmap);
using Graphics graphics = Graphics.FromImage(bmp);
using Brush brush = new SolidBrush(Color.Red);
using Pen pen = new Pen(Color.Red);
for (int i = 0; i < circles.Length; i++)
{
var circle = circles[i];
//var d = circle.Radius * 2;
graphics.FillEllipse(brush, new RectangleF(circle.Center.X - 9, circle.Center.Y - 9, 18, 18));
//graphics.DrawEllipse(pen, new Rectangle(6, 6, 30, 30));
//graphics.DrawRectangle(pen, new Rectangle(30, 36, 50, 50));
}
this.pictureBox1.Image = bmp;
}
/*public static CircleF[] HoughCircles(
IInputArray image,//输入图像,8位单通道灰度图像
HoughType method,//检测方法使用。目前,唯一实现的方法是CV_HOUGH_GRADIENT
double dp,//累加器分辨率与图像分辨率的反比。例如,如果dp = 1,则累加器具有与输入图像相同的分辨率。如果dp = 2,则累加器的宽度和高度都是一半
double minDist,//检测到的圆的中心之间的最小距离。太小会多检,太大会漏检
double param1 = 100,//传递给Canny()检测器的两个阈值中的较高的阈值(较高的是较低的两倍左右)
double param2 = 100,//检测阶段圆心的累加器阈值。越小,可得到越多的圆
int minRadius = 0,//最小圆半径
int maxRadius = 0//最大圆半径
)*/
}
/// <summary>
/// 开启二值化处理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked && imgscr != null)
{
// 二值化
CvInvoke.Threshold(imgscr, imgscr, trackBar1.Value, trackBar2.Value, ThresholdType.Binary);
this.pictureBox1.Image = imgscr.Bitmap;
}
}
/// <summary>
/// 开启高斯滤波
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void checkBox2_CheckedChanged(object sender, EventArgs e)
{
if (checkBox2.Checked && imgscr != null && (trackBar4.Value & 1) == 1)
{
CvInvoke.GaussianBlur(imgscr, imgscr, new Size(trackBar4.Value, trackBar3.Value), (double)numericUpDown1.Value, (double)numericUpDown2.Value);
this.pictureBox1.Image = imgscr.Bitmap;
}
}
/// <summary>
/// 重置
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
LoadPicture();
checkBox1.Checked = false;
checkBox2.Checked = false;
}
/// <summary>
/// 霍夫圆检测-minRadius
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void trackBar6_Scroll(object sender, EventArgs e)
{
StartHoughCircles();
label14.Text = trackBar6.Value.ToString();
}
/// <summary>
/// 霍夫圆检测-maxRadius
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void trackBar5_Scroll(object sender, EventArgs e)
{
StartHoughCircles();
label13.Text = trackBar5.Value.ToString();
}
/// <summary>
/// 启动圆形网格
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button3_Click(object sender, EventArgs e)
{
LoadCirclesGrid();
}
private void LoadCirclesGrid()
{
if (imgscr != null)
{
//InputArray _image:输入图片; Size patternSize:网格大小; _centers:提取的网格点中心;
//flags:是否为对称网格; blobDetector:斑点检测器; parameters:参数
var feature2D = new SimpleBlobDetector();
var img = imgscr.ToImage<Gray, Byte>();
var circles = CvInvoke.FindCirclesGrid(img, new Size(trackBar8.Value, trackBar7.Value), CalibCgType.SymmetricGrid, feature2D);
if (circles == null)
return;
Bitmap bmp = new Bitmap(imgscr.Bitmap);
Graphics graphics = Graphics.FromImage(bmp);
Brush brush = new SolidBrush(Color.Red);
Pen pen = new Pen(Color.Red);
for (int i = 0; i < circles.Length; i++)
{
var circle = circles[i];
//var d = circle.Radius * 2;
graphics.FillEllipse(brush, new RectangleF(circle.X - 9, circle.Y - 9, 18, 18));
//graphics.DrawEllipse(pen, new Rectangle(6, 6, 30, 30));
//graphics.DrawRectangle(pen, new Rectangle(30, 36, 50, 50));
}
this.pictureBox1.Image = bmp;
pen.Dispose();
brush.Dispose();
graphics.Dispose();
feature2D.Dispose();
img.Dispose();
}
}
/// <summary>
/// 图片路径
/// </summary>
private string imgPath = string.Empty;
/// <summary>
/// 选择图像
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button4_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Multiselect = true;
ofd.Title = "打开一幅图像";
ofd.Filter = "图片(*.jpg,*.gif,*.bmp)|*.jpg;*.gif;*.bmp";
if (ofd.ShowDialog() == DialogResult.OK)
{
imgPath = ofd.FileName;
label21.Text = imgPath;
LoadPicture();
}
}
private void trackBar8_Scroll(object sender, EventArgs e)
{
label25.Text = trackBar8.Value.ToString();
LoadCirclesGrid();
}
private void trackBar7_Scroll(object sender, EventArgs e)
{
label24.Text = trackBar7.Value.ToString();
LoadCirclesGrid();
}
}
结果如下:
网格提取
霍夫圆:
需要代码私。