基EmguCv/OpenCv的零件的缺陷检测

基EmguCv/OpenCv的零件的缺陷检测
思路:

  1. 对图像去噪和二值化处理;
  2. 提取外部轮廓,并填充;
  3. 提取内部轮廓并以另一种颜色填充;
  4. 外轮廓和内轮廓叠加,得到缺陷区域;
  5. 对缺陷区域做二值化处理,并提取轮廓计算缺陷面积;
  6. 标记处缺陷位置。

存在的缺陷:只能检测外测缺陷,无法检测内部缺陷。

效果图

源代码(c#)

  1 using Emgu.CV;
  2 using Emgu.CV.CvEnum;
  3 using Emgu.CV.Structure;
  4 using Emgu.CV.Util;
  5 using System;
  6 using System.Drawing;
  7 using System.IO;
  8 using System.Windows.Forms;
  9 
 10 namespace DefectDetection2
 11 {
 12     public partial class Form1 : Form
 13     {
 14         public Form1()
 15         {
 16             InitializeComponent();
 17         }
 18 
 19         private string[] FileNmae = null;//读取文件夹下的文件
 20         private int times = 0;//上一张/下一张按钮点击次数(对应图片数组的索引)
 21         private int ErodeVaule = 1, DelitVaule = 1;//膨胀/腐蚀运算
 22         private Image<Bgr, byte> picture = null;//原始图片
 23         private int BinVaule = 0;//二值化阈值
 24         private int y = 0, cr_min = 0, cb_min = 0, cr_max = 0,cb_max=0;//YCC颜色阈值
 25         private double area1 = 0, area2 = 0;//缺陷面积
 26 
 27         /// <summary>
 28         /// Erode值
 29         /// </summary>
 30         /// <param name="sender"></param>
 31         /// <param name="e"></param>
 32         private void domainUpDown1_SelectedItemChanged(object sender, EventArgs e)
 33         {
 34             ErodeVaule = Convert.ToInt16(domainUpDown1.Text);
 35         }
 36 
 37         /// <summary>
 38         /// Delite值
 39         /// </summary>
 40         /// <param name="sender"></param>
 41         /// <param name="e"></param>
 42         private void domainUpDown2_SelectedItemChanged(object sender, EventArgs e)
 43         {
 44             DelitVaule = Convert.ToInt16(domainUpDown2.Text);
 45         }
 46 
 47         /// <summary>
 48         /// 选择图片
 49         /// </summary>
 50         /// <param name="sender"></param>
 51         /// <param name="e"></param>
 52         private void button1_Click(object sender, EventArgs e)
 53         {
 54             OpenFileDialog ofd = new OpenFileDialog();
 55             if (ofd.ShowDialog() == DialogResult.OK)
 56             {
 57                 pictureBox2.Image = Image.FromFile(ofd.FileName);
 58                 picture = new Image<Bgr, byte>(ofd.FileName);
 59                 Image <Bgr ,byte >pic= new Image<Bgr, byte>(PicSubtraction(ContourFilling(ToBin(picture)), ContourFilling2(ToBin(picture))).Bitmap);
 60                 pictureBox1.Image = ContourFilling3(pic).Bitmap;
 61                 label4.Text = Path.GetFileName(ofd.FileName);
 62             }
 63         }
 64 
 65         /// <summary>
 66         /// 上一张
 67         /// </summary>
 68         /// <param name="sender"></param>
 69         /// <param name="e"></param>
 70         private void button2_Click(object sender, EventArgs e)
 71         {
 72             FileNmae = Directory.GetFiles(@"D:workHCI工件样品");
 73             times--;
 74             if (times < 0)
 75                 times = FileNmae.Length - 1;
 76             picture = new Image<Bgr, byte>(FileNmae[times]);
 77             pictureBox2.Image = Image.FromFile(FileNmae[times]);
 78             label4.Text = Path.GetFileName(FileNmae[times]);
 79             Image<Bgr, byte> pic = new Image<Bgr, byte>(PicSubtraction(ContourFilling(ToBin(picture)), ContourFilling2(ToBin(picture))).Bitmap);
 80             pictureBox1.Image = ContourFilling3(pic).Bitmap;
 81         }
 82 
 83         /// <summary>
 84         /// 下一张
 85         /// </summary>
 86         /// <param name="sender"></param>
 87         /// <param name="e"></param>
 88         private void button3_Click(object sender, EventArgs e)
 89         {
 90             FileNmae = Directory.GetFiles(@"D:workHCI工件样品");
 91             times++;
 92             if (times > FileNmae.Length - 1)
 93                 times = 0;
 94             picture = new Image<Bgr, byte>(FileNmae[times]);
 95             pictureBox2.Image = Image.FromFile(FileNmae[times]);
 96             label4.Text = Path.GetFileName(FileNmae[times]);
 97             Image<Bgr, byte> pic = new Image<Bgr, byte>(PicSubtraction(ContourFilling(ToBin(picture)), ContourFilling2(ToBin(picture))).Bitmap);
 98             pictureBox1.Image = ContourFilling3(pic).Bitmap;
 99         }
100 
101         /// <summary>
102         /// 二值化
103         /// </summary>
104         /// <param name="pic">输入Bgr图片</param>
105         /// <returns></returns>
106         private Image<Gray, byte> ToBin(Image<Bgr, byte> pic)
107         {
108             Image<Gray, byte> outpic = pic.Convert<Gray, byte>();
109             outpic = outpic.ThresholdBinary(new Gray(100), new Gray(255));
110             outpic = outpic.Erode(ErodeVaule);
111             outpic = outpic.Dilate(DelitVaule);
112             return outpic;
113         }
114 
115         /// <summary>
116         /// 补全轮廓并填充
117         /// </summary>
118         /// <param name="pic">输入灰度图</param>
119         /// <returns></returns>
120         private Image<Bgr, byte> ContourFilling(Image<Gray, byte> pic)
121         {
122             Image<Bgr, byte> outpic = new Image<Bgr, byte>(pic.Size);
123             pic = pic.Canny(100, 255);
124             Image<Gray, byte> outcon = new Image<Gray, byte>(pic.Size);
125             VectorOfVectorOfPoint con = new VectorOfVectorOfPoint();
126             CvInvoke.FindContours(pic, con, outcon, RetrType.External, ChainApproxMethod.ChainApproxNone);
127             Point[][] con1 = con.ToArrayOfArray();
128             PointF[][] con2 = Array.ConvertAll(con1, new Converter<Point[], PointF[]>(PointToPointF));
129             PointF[] hull = new PointF[con[0].Size];
130             for (int i = 0; i < con.Size; i++)
131             {
132                 hull = CvInvoke.ConvexHull(con2[i], true);
133                 for (int j = 0; j < hull.Length; j++)
134                 {
135                     Point p1 = new Point((int)(hull[j].X + 0.5), (int)(hull[j].Y + 0.5));
136                     Point p2;
137                     if (j == hull.Length - 1)
138                     {
139                         p2 = new Point((int)(hull[0].X + 0.5), (int)(hull[0].Y + 0.5));
140                     }
141                     else
142                         p2 = new Point((int)(hull[j + 1].X + 0.5), (int)(hull[j + 1].Y + 0.5));
143                     CvInvoke.Line(outpic, p1, p2, new MCvScalar(255, 0, 255, 255), 2, 0, 0);
144                 }
145             }
146 
147             Image<Gray, byte> gray = new Image<Gray, byte>(pic.Size);
148             gray = outpic.Convert<Gray, byte>();
149             gray = gray.ThresholdBinary(new Gray(100), new Gray(255));
150             gray = gray.Canny(100, 255);
151             VectorOfVectorOfPoint con3 = new VectorOfVectorOfPoint();
152             CvInvoke.FindContours(gray, con3, outcon, RetrType.External, ChainApproxMethod.ChainApproxNone);
153             for (int i = 0; i < con3.Size; i++)
154             {
155                 CvInvoke.DrawContours(outpic, con3, i, new MCvScalar(255, 0, 0), -1);
156             }
157 
158             return outpic;
159         }
160 
161         /// <summary>
162         /// 填充缺陷轮廓
163         /// </summary>
164         /// <param name="pic"></param>
165         /// <returns></returns>
166         private Image<Bgr, byte> ContourFilling2(Image<Gray, byte> pic)
167         {
168             Image<Bgr, byte> outpic = new Image<Bgr, byte>(pic.Size);
169             pic = pic.Canny(100, 255);
170             Image<Gray, byte> outcon = new Image<Gray, byte>(pic.Size);
171             VectorOfVectorOfPoint con = new VectorOfVectorOfPoint();
172             CvInvoke.FindContours(pic, con, outcon, RetrType.External, ChainApproxMethod.ChainApproxNone);
173             for (int i = 0; i < con.Size; i++)
174             {
175                 CvInvoke.DrawContours(outpic, con, i, new MCvScalar(0, 255, 255, 0), -1);
176             }
177             for (int i = 0; i < con.Size; i++)
178             {
179                 CvInvoke.DrawContours(outpic, con, i, new MCvScalar(0, 255, 255, 0),10);
180             }
181             return outpic;
182         }
183 
184         /// <summary>
185         /// 叠加图像
186         /// </summary>
187         /// <param name="pic1">输入Bgr图像1</param>
188         /// <param name="pic2">输入Bgr图像2</param>
189         /// <returns></returns>
190         private Image<Bgr, byte> PicSubtraction(Image<Bgr, byte> pic1, Image<Bgr, byte> pic2)
191         {
192             Image<Bgr, byte> outpic = new Image<Bgr, byte>(pic1.Size);
193             pic1 = ContourFilling(ToBin(picture));
194             pic2 = ContourFilling2(ToBin(picture));
195             CvInvoke.AddWeighted(pic1, 0.5, pic2, 0.5, 1, outpic);
196 
197             return outpic;
198         }
199 
200         /// <summary>
201         /// Point转换为PointF
202         /// </summary>
203         /// <param name="pt">输入Point</param>
204         /// <returns></returns>
205         private PointF[] PointToPointF(Point[] pt)
206         {
207             PointF[] aaa = new PointF[pt.Length];
208             int num = 0;
209             foreach (var point in pt)
210             {
211                 aaa[num].X = point.X;
212                 aaa[num++].Y = (int)point.Y;
213             }
214             return aaa;
215         }
216 
217         /// <summary>
218         /// 填充缺陷轮廓
219         /// </summary>
220         /// <param name="pic">输入Bgr图像</param>
221         /// <returns></returns>
222         private Image<Bgr, byte> ContourFilling3(Image<Bgr, byte> pic)
223         {
224             Image<Bgr, byte> outpic = new Image<Bgr, byte>(pic.Size);
225             Image<Ycc, byte> ycc = pic.Convert<Ycc, byte>();
226             for(int i=0;i<ycc.Height;i++)
227                 for(int j=0;j<ycc.Width;j++)
228                 {
229                     if (ycc[i, j].Cr > 35 && ycc[i, j].Cr < 148 &&
230                         ycc[i, j].Cb > 48 && ycc[i, j].Cb < 141)
231                     {
232                         ycc[i, j] = new Ycc(0, 0, 0);
233                     }
234                     else ycc[i, j] = new Ycc(255, 255, 255);
235                 }
236             Image<Gray, byte> gray = ycc.Convert<Gray, byte>();
237             gray = gray.ThresholdBinary(new Gray(100 ), new Gray(255));
238             gray = gray.Canny(100, 60);
239             Image<Gray, byte> outcon = new Image<Gray, byte>(pic.Size);
240             VectorOfVectorOfPoint con = new VectorOfVectorOfPoint();
241             CvInvoke.FindContours(gray, con, outcon, RetrType.External, ChainApproxMethod.ChainApproxNone);
242             int n=0;
243 
244                 for (int i = 0; i < con.Size; i++)
245                 {
246                     if (CvInvoke.ContourArea(con[i]) >0)
247                     {
248                         n++;
249 
250                     }
251                 }
252             textBox1.Text = "" + n.ToString() + "个缺陷"+"      ";
253             n = 0;
254             for (int i = 0; i <con .Size ; i++)
255             {
256                 if (CvInvoke.ContourArea(con[i]) >0)
257                 {
258                     CvInvoke.DrawContours(outpic, con, i, new MCvScalar(0, 255, 0), 5);
259                     textBox1.Text = textBox1.Text + "" + (++n).ToString() + "个缺陷的面积为" + CvInvoke.ContourArea(con[i]);
260                 }
261             }
262             CvInvoke.AddWeighted(outpic , 0.5,picture , 0.5, 0, outpic);
263             return outpic;
264         }
265 
266     }
267 }
原文地址:https://www.cnblogs.com/ybqjymy/p/13902008.html