赞
踩
目录
1. 由于受到软件著作权保护,本视觉检测系统仅提供部分算法设计思路。该系统建议作为一个框架使用,通过调整算法结构,具体参数和文件路径实现视觉检测。
2. 完整版本已投入工业现场使用。
本系统主要针对PCB进行检测,PCB在焊锡过程中可能出现以下问题:
- (1)焊点缺失
- (2)焊点粘连
因此提出以下要求:
- (1)对PCB上的焊点进行检测
- (2)对所有焊点进行判断是否存在缺陷,有缺陷则报警,无缺陷则显示合格
- (3)将视觉检测系统与PLC进行通讯,通过三色灯显示检测结果
名称 | 型号规格 |
高清工业相机 | AA1300 -90GC |
相机镜头 | HF1628-6MP |
LED面光源 | BL120*60 |
光源控制器 | 模拟恒流 |
工控机 | CV-A6-08128 |
IO控制器 | DAM0808D |
检测思路:
模块介绍:
1.通过python内置的os库来进行文件读取,获得目标文件夹长度list,通过len(list)计算文件夹长度。
2.当目标文件夹长度不为1时,显示长度(文件夹内图片数量),通过for循环递增文件夹长度的倒数第二个位置,用os.Remove()将图片删除,剩下最后一张即最新的图片。
3.当目标文件夹长度为1时,说明相机驱动软件MVS采集到最新图片,直接pass进入下一模块。
模块介绍:
1.图片在计算机中以数组形式显示,相机分辨率为1280*1024,受限于焦距和光圈限制,不能完全覆盖PCB大小,因此需要把 1280*1024的原图裁剪成 和PCB大小相同的 300*350 的图片。PCB板的焊锡分布为上面五个,中间三个,下面三个,生成目标区域的掩膜mask,并且将掩膜像素全置为255,三个mask区域大小分别为:
2.通过位操作cv.bitwise_and将图像和掩膜进行合并,分别提取出三个目标区域,最后通多cv.add()将区域相加,使得原本图像只剩下三个目标区域:
模块介绍:
用black-pixel和white-pixel两个变量储存黑白像素点的数量,通过遍历1280*1024即13,107,200个像素,使用PLI包内的Image模块,image.size()用于读取PIL方式image.open()打开的图片,该模式下读取图片的宽和长,将值返回到width和height两个变量中。通过image.getpixel()获取某点处像素,统计图片画幅中的像素点数量,当遍历完所有像素后,计算黑白占比并显示,流程结束。
图像处理模块是一个线性流程,其中的图像处理算法为灰度处理 - 形态学处理(腐蚀 - 膨胀 - 开运算)- 高斯滤波 - 轮廓提取,分别使用到以下函数:
表2 — 图像处理函数表
类型 | 函数名称 | 结构元大小 |
腐蚀 | cv.erode() | (5,5) |
膨胀 | cv.dilate() | (3,3) |
开运算 | cv.morphologyEx() | (7,7) |
高斯滤波 | cv.GaussianBlur() | (1,1) |
二值化 | cv.threshold() | |
发现轮廓 | cv.findContours() | |
绘制轮廓 | cv.drawContours() |
具体函数的使用如下:
处理前图片为PCB板,因涉及专利设计保护,因此使用SW模型代替。
图1 处理前图片 图2 处理后图片
图3 检测结果显示
图4 处理前图片 图5 处理后图片
图6 处理前图片
- import os
- import time
- import cv2 as cv
- import numpy as np
- from PIL import Image
- from appdirs import unicode
-
- '''
- 算法设计:
- (1)形态学处理 -- 1、(腐蚀5*5,膨胀3*3,开运算7*7,均为矩形结构元) 2、高斯滤波
- (2)blob筛选 -- 提取并绘制轮廓,减少图片噪声
- '''
-
- '''
- 检测流程:
- (1)从文件夹内读取RGB图片
- (2)对RGB图片进行灰度处理,形态学处理,高斯滤波和轮廓提取,其中灰度处理后需要进行掩膜处理,提取目标区域.
- (3)对处理完的图片进行黑白占比计算,焊锡点缺失会导致焊锡处反光,导致亮度过高.当白色占比超过阈值,判断为焊锡缺失
- (4)输出合格与不合格后,与PLC通过modbus通讯
- '''
-
- def cut(cut):
- '''
- 主要功能:把 1024*1280 的原图 裁剪成 PCB大小的 300*350 的图片
- 然后将三个焊锡区域提取出来并合并
- '''
- img = cut[350:650, 500:850] # 把 1280*1024 的原图 裁剪成 PCB大小的 300*350 的图片
-
- # 分割区域1 -- 上面五个焊锡点
- mask1 = np.zeros(img.shape[:2], np.uint8)
- mask1[10:60, 10:330] = 255
- mask1_img = cv.bitwise_and(img, img, mask=mask1)
- # cv.imshow('mask1',mask1_img)
-
- # 分割区域2 -- 中间三个焊锡点
- mask2 = np.zeros(img.shape[:2], np.uint8)
- mask2[80:130, 70:270] = 255
- mask2_img = cv.bitwise_and(img, img, mask=mask2)
- # cv.imshow('mask2',mask2_img)
-
- # 分割区域3 -- 下面三个焊锡点
- mask3 = np.zeros(img.shape[:2], np.uint8)
- mask3[240:290, 70:270] = 255
- mask3_img = cv.bitwise_and(img, img, mask=mask3)
- # cv.imshow('mask3',mask3_img)
-
- pic1 = cv.add(mask1_img, mask2_img) # 区域1+区域2
- pic = cv.add(pic1, mask3_img) # 区域(1+2)+区域3
-
- return pic
-
- def remove():
- '''
- 主要功能:保证文件夹内只有最新的那一张图片
- '''
- # 时间在0.01以内,因此不记录时间
- path = r'C:\Users\-libr\Desktop\PCB\PCB input_dir'
- file = unicode(path)
- files = os.listdir(file) # 创建由文件名组成的列表
- #print('filelist:', files)
-
- if len(os.listdir(path)) > 1:
- print(f"图片数量为:{len(os.listdir(path))}")
- for i in range(len(os.listdir(path))-1):
- #设置 len(os.listdir(path))-1 是为了保证i递增到倒数第二,留下最新的图片
- i += 1
- #print(i)
- os.remove(os.path.join(path, f'PCB{i}.jpg'))
-
- elif len(os.listdir(path)) == 1:
- print(f"图片数量为:1 ")
- #文件夹内只有最新的那张图片
- pass
-
- def count(image):
- '''
- 主要功能;输入单张图片图片,计算并输出'黑白占比的计算结果'和'计算一次的时间'
- '''
-
- start = time.time()
- white_pixel = 0
- black_pixel = 0
-
- width, height = image.size
- for i in range(width):
- for j in range(height):
- if image.getpixel((i,j)):
- white_pixel += 1
- else:
- black_pixel += 1
-
- black = black_pixel / (width * height)
- white = white_pixel / (width * height)
-
- end = time.time()
- cost = end - start
-
- print('黑白检测时间:%s'%cost)
- print('黑色占比:%s\n白色占比:%s'%(black,white))
-
- #判断黑白占比阈值,输出检验结果
- if black > 0.99:
- return(print('合格\n'))
- else:
- return(print('不合格\n'))
-
- def convert(input_dir, output_dir):
- for filename in os.listdir(input_dir):
- '''
- 主要功能:读取文件夹内的图片,进行图像处理后,并将其保存到指定文件
- '''
- path = input_dir + "/" + filename # 获取文件路径
- gray_img = cv.imread(path, 0) # 读取图片,参数0为直接以灰度模式读取
-
- cut_pic = cut(gray_img) #提取三个焊锡区域后的图片
-
- # 腐蚀
- kernel1 = cv.getStructuringElement(cv.MORPH_RECT, (5, 5))
- img_erode = cv.erode(cut_pic, kernel1)
-
- # 膨胀
- kernel2 = cv.getStructuringElement(cv.MORPH_RECT, (3, 3))
- img_dilate = cv.dilate(img_erode, kernel2)
-
- # 开运算
- kernel3 = cv.getStructuringElement(cv.MORPH_RECT, (7, 7))
- img_open = cv.morphologyEx(img_dilate, cv.MORPH_OPEN, kernel3, iterations=1)
-
- # 高斯滤波
- img_gaussian = cv.GaussianBlur(img_open, (1, 1), 2)
-
- # 二值化
- ret, img_turn = cv.threshold(img_gaussian, 127, 255, 0)
-
- # 发现轮廓
- contours, heriachy = cv.findContours(img_turn, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
- for i, contour in enumerate(contours):
- # 绘制每条轮廓
- cv.drawContours(img_turn, contours, i, (0, 0, 255), 2)
-
- cv.imwrite(output_dir + '/' + filename, img_turn)
- cv.imshow("PCB", img_turn)
- cv.waitKey()
- cv.destroyAllWindows()
- # cv.destroyAllWindows(1)
-
- def read_path(output_dir):
- for filename in os.listdir(output_dir):
- '''
- 主要功能:定义读取路径的函数,为用PIL打开图片获得
- '''
- path = output_dir + "/" + filename # 获取文件路径
- image = Image.open(path).convert('L')
- count(image)
-
- if __name__ == '__main__':
-
- # 输入RGB图片
- input_dir = r'C:\Users\-libr\Desktop\PCB\PCB input_dir' # 输入文件夹
- output_dir = r'C:\Users\-libr\Desktop\PCB\PCB output_dir' # 输出文件夹
-
- #1、保持文件夹内只有最新图片
- remove()
-
- #2、对图片进行读取,处理和保存
- convert(input_dir, output_dir)
-
- #3、对图片进行黑白占比计算并判断
- read_path(r'C:\Users\-libr\Desktop\PCB\PCB output_dir')
-
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。