基于opencv的贴片机散料识别

一、需求提出

1、发文初衷

        在贴片机研发过程中,一直有一种非常小众的需求,即散料识别。正好本人为一家公司定制过散料识别核心算法,基于opencv的开源精神,本人将代码的开源,希望对相同需求的开发有抛砖引玉的作用。

2、需求背景

        贴片机一般使用元件料为编带封包。使用喂料器固定栈位贴装。散料应用场景,一般要除去规律包装,PCB打样,或者军品等二次筛选高品质元件,多数为人工贴装,其中的不可控因素可想而知。只要使用视觉算法,将元件的位置告诉贴片机,贴片机即可拾取贴装。

3、算法应用场景

        同规格散料,非堆叠分布在托盘中,视觉识别这些元件,相对非常容易,而且能覆盖大部分的现场需求。

二、opencv算法过程

1、opencv版本为3.4,编程环境为QT5,编程平台为win10

2、识别结果

        如图所示,1K-0805电阻,分散于白色面上,最终视觉识别出电阻的中心位置和短边角度,这样贴片机就可以准确拾取并贴装。

3、算法过程

2.1 首先将要识别的图片读入、并转换为灰度图片,以便后续高效处理

    cv::Mat imgdata = cv::imread("bulk.jpg",IMREAD_COLOR);

    cv::Mat grayImgdata;
    cv::cvtColor(imgdata,grayImgdata,COLOR_BGR2GRAY);

2.2 图形双边滤波,去除图像中噪点,但保留边缘特征

    cv::Mat bilateralMat;
    cv::bilateralFilter(grayImgdata,bilateralMat,3,10,10);

具体效果如图所示,肉眼基本分辨不出图像改善,但对于图像噪点较多时,此步骤将特别有用

2.3 图像二值化,将图形处理为二值化图形,更方便识别轮廓,方便提取位置

    cv::Mat threshold1;
    cv::threshold(bilateralMat,threshold1,thresh1,255,THRESH_BINARY_INV);

图像二值化以后,基本的电阻位置已经非常明显,但在电阻的周围,我们发现有许多斑斑点点,还有“102”字样也需要处理

2.4 腐蚀与膨胀操作

    cv::Mat morphologyMat;
    cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(2, 2));
    //cv::morphologyEx(threshold1,morphologyMat,MORPH_CLOSE,element);
    cv::erode(threshold1,morphologyMat,element);//侵蚀
    element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(4, 4));
    cv::dilate(morphologyMat,morphologyMat,element);//扩展

图中可以看到,电阻周围的斑点,电阻的规格“102”字符,基本处理干净,个别电阻上偶尔还会留有黑色斑点,还需进一步处理

2.5 中值滤波

    cv::Mat median;
    cv::medianBlur(morphologyMat,median,3);

中值滤波以后,图形已经非常完美,每个电阻位置分明,电阻位置清晰可见

2.6 轮廓查找

    std::vector<std::vector<Point>> contours;
    std::vector<Vec4i> hierarchy;
    cv::findContours(median, contours, hierarchy, RETR_LIST, CHAIN_APPROX_SIMPLE);

如图所示,通过轮廓识别,所有的电阻已经被成功圈出,其中也包括一个圆形的人为干扰项

2.7 通过轮廓面积,过滤不符合规定的元件

    // 按面积过滤
    std::vector<std::vector<Point> > resultContours;

    int areaMax = 1700;
    int areaMin = 1100;
    for (quint64 i=0; i<contours.size(); i++) {
        //轮廓面积
        double area = cv::contourArea(contours.at(i));
      
        if(area >= areaMin && area <= areaMax){
            resultContours.push_back(contours.at(i));
        }
    }

因为元件的每种封装大小,对应图像中的面积不同,图中0805对应的面积大约在1100-1700范围内。对于一台准备售出的贴片机而言,相机的分辨率,相机安装参数等,都已经固定,在封装设置时,可以根据实际情况,调整每种封装的面积范围,达到过滤的目的。

2.8 轮廓的位置查找

    for(quint64 i=0;i<resultContours.size();i++){
        RotatedRect rrect = cv::minAreaRect(resultContours.at(i));

        drawRotatedRectMark(colorImgdata,rrect);
    }

opencv的minAreaRect函数,可以帮助我们找到最小外接的矩形,此矩形中记录这元件的中心位置和角度信息,本例调用自处理函数drawRotatedRectMark(),对位置角度信息进行绘制显示

2.9 最小外接矩形处理与绘制

drawRotatedRectMark()代码

void drawRotatedRectMark(const Mat &imgdata, RotatedRect &rrect)
{
    // 画出角度矩形
    Point2f points[4];
    rrect.points(points);
    for (int j = 0; j < 4; ++j) {
        cv::line(imgdata, points[j], points[(j + 1) % 4], Scalar(200,100,200), 2);
    }

    //画出中心点
    cv::circle(imgdata, rrect.center, 3, Scalar(200,200,200), 2);

    double markAngle;
    //规范 width 要大于 height
    if(rrect.size.width < rrect.size.height){
        rrect.angle += 90;
        float tmp = rrect.size.width;
        rrect.size.width = rrect.size.height;
        rrect.size.height = tmp;
    }

    markAngle = rrect.angle - 90.0;

    //画出方向
    cv::line(imgdata,
             rrect.center,
             Point2f(rrect.center.x + 1.5 * rrect.size.height / 2.0 * cos(markAngle*3.1416/180),
                     rrect.center.y + 1.5 * rrect.size.height / 2.0 * sin(markAngle*3.1416/180)),
             Scalar(255,255,100),
             2);
}

由于cpencv算法的原因,还需要对长短边进行进一步处理,本例中处理完成后,直接绘制了元件的中心和短边角度指示

三、总结

        从文中可以得出,opencv对散料图形的处理实际非常简单,而且这种方式对贴片机元件的处理逻辑,基本相同,只需根据贴片机实际情况,设置一次封装的面积大小,可以达到识别所有散料的目的,通用行非常强。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值