芯片残缺检测
本项目旨在通过计算机视觉技术,自动检测芯片表面是否存在缺陷。该系统适用于生产线上多种尺寸的矩形芯片,能够快速且准确地识别出有瑕疵的产品。
技术栈
- Python 3:作为主要开发语言。
- OpenCV:用于图像处理和特征检测的核心库。
- Tkinter:用于创建用户交互界面。
- 其他常用Python库:辅助完成各种功能。
应用场景
在现代制造业中,对于芯片的质量控制至关重要。由于芯片生产过程中的任何微小瑕疵都可能导致产品失效,因此自动化检测设备的需求日益增加。此项目利用高精度的机械定位与高质量的图像采集技术相结合,确保了每次拍摄到的芯片位置一致,从而简化了后续的图像处理步骤。
检测流程
-
预处理阶段:
- 用户首先通过图形界面在一张标准的模板图片上手动标注出芯片的位置边界。
- 使用OpenCV库中的Harris角点检测算法,依据用户的标注信息,在模板图片上生成精确的检测框。
- 用户检查生成的检测框是否正确覆盖了芯片的所有区域。如果不满意,则可以调整标注直至满意为止。
-
图像处理阶段:
- 对于每一张新的芯片图像,系统会自动将其与模板图片进行对比。
- 图像会被转换成黑白二值图像,以便更容易地识别出缺陷区域。
- 通过与模板图像中的检测框相对照,系统能够迅速定位并分析芯片的完整性和质量。
-
缺陷识别阶段:
- 一旦检测框确定,系统将对框内的区域进行深入分析,查找是否有任何异常。
- 如果发现任何疑似缺陷,系统将标记这些区域供人工复查或者直接标记为不合格品。
优势
- 高效性:自动化检测大大提高了检测速度。
- 准确性:通过精确的图像处理算法减少误报率。
- 灵活性:能够适应不同类型和大小的芯片检测需求。
- 易用性:友好的用户界面使非专业人员也能轻松操作。
结论
该项目为制造业提供了先进的芯片缺陷检测解决方案,有助于提升产品质量和生产效率,同时也降低了因人工检测带来的错误率。
环境
- python3
- python的opencv环境
- tkinter
- 其他python常用库
前提
- 每张图片上芯片位置可以认为基本不变.这个由外部机械精度保持
- 拍摄的图片采用高光处理,黑白分明,因此可以二值化处理
- 芯片类型不同,要处理大小不同的各种芯片,但都为矩形
流程
预处理
- 用户在模板图片上,标定框芯片位置
- 根据用户标定位置,使用opencv Harris脚点检测算法,在模板图上生成实际的检测框
- 用户判断检测框是否满足要求,否则重新进行1
检测
当检测到出问题的芯片时,将这张出问题图片保存.符合要求的芯片画矩形,不满足要求画X.结果类似如下.
原理
根据用户划定的矩形框做Harris脚点检测,判断出芯片的四角
芯片位置基本不变,因此可以认为每张图片,芯片位置都固定.将用户的数据序列化保存.
因此用户在模板上画完后,可以认为这个坑中会来对应的芯片.
芯片残缺则在图片上可以显示为白色.图片二值化后,对对应位置的坑位中的图像,计算黑色像素点数量.当不满足阈值时,认为芯片残缺.
此阈值用户可调
优化
直接划定刚刚好检测区域的方法鲁棒性较差.因此优化为可以在检测区域中添加白边(不处理区域).
实际检测中,检测的区域为实际区域+白边大小,但是会扣除白边的像素点.然后对认为有效的区域求黑色像素所占比重.
此添加白边的方法可以应对芯片略微旋转,平移的问题。
main.py
import tkinter as tk
import os
import time
from PIL import Image
from tkinter import messagebox
import pickle
import NestCreateModel
import ConfirmSatisfy
import AnalysePhotoMatchChip
# 绘制第1行
def drawline1():
ChipPosLeftDown = (753, 302)
ChipPosRightDown = (943, 302)
curmacro_pos = (1, 3)
print('draw line 1')
NestCreateModel.CreateLabelBoxLine(OriginImagePath='Origin.jpg',
TxtPath='ChipInfo.txt', ChipPosLeftDown=ChipPosLeftDown,
ChipPosRightDown=ChipPosRightDown, curmacro_pos=curmacro_pos,
DrawBoxOfChip='Below')
sat = tk.messagebox.askyesno(title='计算结果', message='您对这个结果是否满意')
ConfirmSatisfy.CustConfirmPosDict(sat, 'CustomerDefine5')
# 绘制第2行
def drawline2():
ChipPosLeftDown = (753, 905)
ChipPosRightDown = (943, 905)
curmacro_pos = (2, 3)
print('draw line 2')
NestCreateModel.CreateLabelBoxLine(OriginImagePath='Origin.jpg',
TxtPath='ChipInfo.txt', ChipPosLeftDown=ChipPosLeftDown,
ChipPosRightDown=ChipPosRightDown, curmacro_pos=curmacro_pos,
DrawBoxOfChip='Above')
sat = tk.messagebox.askyesno(title='计算结果', message='您对这个结果是否满意')
ConfirmSatisfy.CustConfirmPosDict(sat, 'CustomerDefine5')
def showdict():
print('用户生成的模版字典')
foldername = 'CustomerDefineModel'
dictname = 'CustomerDefine5.pickle'
# pickle格式读取文件
with open(foldername + '/' + dictname, 'rb')as dictfile:
dict2show = pickle.load(dictfile)
print(dict2show)
def showtestimg():
imglist = [x for x in os.listdir('.') if '.jpg' in x]
for i in imglist:
img = Image.open(i)
w, h = img.size
img = img.resize((w // 4, h // 4))
# time.sleep(1)
img.show()
def showtestresult():
foldername = 'CustomerDefineModel'
dictname = 'CustomerDefine5.pickle'
# pickle格式读取文件
with open(foldername + '/' + dictname, 'rb')as dictfile:
modeldict = pickle.load(dictfile)
# 字典中的一些值要修改
ChipSize_tmp = modeldict.get('ChipSize')
WhiteBorder_tmp = modeldict.get('WhiteBorder')
WhiteBorderBoxArea_tmp = (ChipSize_tmp[0] + WhiteBorder_tmp) * (ChipSize_tmp[1] + WhiteBorder_tmp)
# 计算两种BoxArea的值并写入字典中
modeldict['BoxArea'] = ChipSize_tmp[0] * ChipSize_tmp[1]
modeldict['WhiteBorderBoxArea'] = WhiteBorderBoxArea_tmp
imglist = [x for x in os.listdir('.') if '.jpg' in x]
for TestImage in imglist:
DamageLocation = AnalysePhotoMatchChip.StartAnalysisImage(TestImage, similarity=0.12,
Dict=modeldict)
img = AnalysePhotoMatchChip.DrawRectangeWithWhiteBorder(Image.open(TestImage),
DamageLocation, modeldict,
LineWidth=4)
w, h = img.size
img = img.resize((w // 4, h // 4))
img.show()
window = tk.Tk()
window.title('芯片残缺检测')
window.geometry('300x200')
ButtonLine1 = tk.Button(window, height=2, text='计算第1行', command=drawline1)
ButtonLine1.pack()
ButtonLine2 = tk.Button(window, height=2, text='计算第2行', command=drawline2)
ButtonLine2.pack()
ButtonShowDict = tk.Button(window, height=2, text='展示生成的模版字典', command=showdict)
ButtonShowDict.pack()
ButtonShowTest = tk.Button(window, height=2, text='展示待检测图片', command=showtestimg)
ButtonShowTest.pack()
ButtonShowResult = tk.Button(window, height=2, text='展示检测结果', command=showtestresult)
ButtonShowResult.pack()
if __name__ == '__main__':
window.mainloop()