基EmguCv/OpenCv的零件的缺陷检测
思路:
- 对图像去噪和二值化处理;
- 提取外部轮廓,并填充;
- 提取内部轮廓并以另一种颜色填充;
- 外轮廓和内轮廓叠加,得到缺陷区域;
- 对缺陷区域做二值化处理,并提取轮廓计算缺陷面积;
- 标记处缺陷位置。
存在的缺陷:只能检测外测缺陷,无法检测内部缺陷。
效果图
源代码(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 }