一、基本情况 在较好的成像质量下,可以通过ROI直接完成初略定位;而后通过手工定义特征提取信息;完成量化和评价。
这次项目能够实现的首要原因: 一是因为在已经布置了成像系统,能够采集获得高质量图片; 二是在图像处理领域的积累,让我能够解决一些问题。 这是我第一次较为系统地解决“视觉检测”问题,我认为解决了一些问题,有很多收获。 二、技术细节 0、ROI定位,使用MarkMan+NotePade,非常高效;
1、左侧大螺丝 通过HoughCircles能够非常准确地识别出螺丝孔洞和异常情况。这个思路的来源是因为在做钢管识别的过程中,对几个找圆算法进行了分析研究。
screwMat[i] = src(cv::Rect(screwLocation[i], screwSize[i])); vector<KeyPoint> keypoints; detector->detect(screwMat[i], keypoints); if (keypoints.size() == 0) { Mat gray; Mat bin; vector<Vec3f> vec3f_method_hough; cvtColor(screwMat[i], gray, COLOR_BGR2GRAY); threshold(gray, bin, 100, 255, THRESH_OTSU); HoughCircles(gray, vec3f_method_hough, HOUGH_GRADIENT, 2, p_radius * 2, 100, 33, p_radius - 2, p_radius + 2); if (vec3f_method_hough.size() >= 1) putText(draw, "Y1", screwLocation[i], FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(255, 0, 0), 7); else { //当探测到 ERROR的时候推出循环 putText(draw, "E1", screwLocation[i], FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(0, 255, 0), 7); bError = true; } vec3f_method_hough.clear();
2、左侧大螺丝防护罩 采用的是阈值、投影的方法进行解决。这个思路的来源,应该是答题卡。
//识别螺丝防护罩 coverMat[i] = src(Rect(coverLocation[i], coverSize[i])); cvtColor(coverMat[i], tmpCoverMat[i], COLOR_BGR2GRAY); threshold(tmpCoverMat[i], tmpCoverMat[i], 100, 255, THRESH_BINARY); //取中间一列的投影 Mat tmp = tmpCoverMat[i].col(tmpCoverMat[i].cols / 2); int istart = 0; int iend = tmp.rows - 1; //开头 for (int irow = 0; irow < tmp.rows - 3; irow++) { if (0 == tmp.at<uchar>(irow, 0) && tmp.at<uchar>(irow + 1, 0) > 0 && tmp.at<uchar>(irow + 2, 0) > 0 && tmp.at<uchar>(irow + 3, 0) > 0) { istart = irow; break; } } //结尾 for (int irow = tmp.rows - 1; irow > 4; irow--) { if (0 == tmp.at<uchar>(irow, 0) && tmp.at<uchar>(irow - 1, 0) > 0 && tmp.at<uchar>(irow - 2, 0) > 0 && tmp.at<uchar>(irow - 3, 0) > 0) { iend = irow; break; } } if (istart <= tmp.rows * 0.1 || iend >= tmp.rows*0.92)//注意这里的0.1和0.9可能是需要设置的 { putText(draw, "N2", cv::Point(coverLocation[i].x + 150, coverLocation[i].y), FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(0, 0, 255), 7); }
3、白色隔弧板 阈值、轮廓(面积)分析的方法进行。这个思路的来源,也应该是钢管识别。
//在正确的基础上,进一步识别白色隔弧板 wbMat[i] = src(Rect(whiteBoardLocation[i], whiteBoardSize[i])); cvtColor(wbMat[i], wbMat[i], COLOR_BGR2GRAY); threshold(wbMat[i], wbTmpMat[i], 50, 255, THRESH_OTSU); //1、去沾粘; cv::rectangle(wbTmpMat[i], cv::Rect(0, 0, wbTmpMat[i].cols, wbTmpMat[i].rows), cv::Scalar(0),3); Mat element = getStructuringElement(MORPH_ELLIPSE, Size(7, 3)); morphologyEx(wbTmpMat[i], wbTmpMat[i], cv::MORPH_OPEN, element); //2、分轮廓 cv::Mat binDraw; int iRight = 0; vector<VP> binVP = connection2(wbTmpMat[i], binDraw); //3、以面积等特征筛选 for (int indexBindVP=0;indexBindVP<binVP.size();indexBindVP++) { double dArea = cv::contourArea(binVP[indexBindVP]); if (dArea >= 250 && dArea <= 480)//可能区间 iRight++; //printf("%d__%f\n", indexBindVP, (float)dArea); }
在实现的过程中,通过系统方式有效去粘连,这个也是来源于之前项目
4、手柄 采用的是阈值、投影的方法进行解决。
Mat handleMat = src(Rect(946, 884, 541, 378)); Mat handleTmp; cvtColor(handleMat, handleTmp, COLOR_BGR2GRAY); threshold(handleTmp, handleTmp, 100, 255, THRESH_OTSU); cv::dilate(handleTmp, handleTmp, Mat()); Mat handleTmpRow = handleTmp.row(handleTmp.rows / 2); int iHandleStart = 0; int iHandleEnd = handleTmpRow.cols - 1; int iHandleUp = 0; //开头 for (int i = 0; i < handleTmpRow.cols - 2; i++) { if (0 == handleTmpRow.at<uchar>(0, i) && handleTmpRow.at<uchar>(0, i + 1) > 0 && handleTmpRow.at<uchar>(0, i + 2) > 0) iHandleUp++; } if (iHandleUp > 7)//超级参数 { putText(draw, "N4", cv::Point(946 + 150, 884), FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(0, 0, 255), 7); } else { putText(draw, "Y4", cv::Point(946 + 150, 884), FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(255, 0, 0), 7); }
5、隔膜 阈值、轮廓(外界矩形长宽比)分析的方法进行。
//识别隔膜 filmMat[i] = src(Rect(filmLocation[i], filmSize[i])); cv::rectangle(draw, Rect(filmLocation[i], filmSize[i]), cv::Scalar(0, 0, 255)); cv::cvtColor(filmMat[i], filmMat[i], COLOR_BGR2GRAY); cv::threshold(filmMat[i], tmpFilmMat[i], 100, 255, cv::THRESH_OTSU); cv::dilate(tmpFilmMat[i], tmpFilmMat[i], cv::Mat()); //1、去粘连 cv::rectangle(tmpFilmMat[i], cv::Rect(0, 0, tmpFilmMat[i].cols, tmpFilmMat[i].rows), Scalar(0)); //2、找最大轮廓 vector<cv::Point> biggestContour = FindBigestContour(tmpFilmMat[i]); Rect boundRect = boundingRect(Mat(biggestContour)); //获得轮廓最小外接矩形 cv::rectangle(draw,cv::Rect(filmLocation[i].x+boundRect.x, filmLocation[i].y+boundRect.y, boundRect.width, boundRect.height), cv::Scalar(0, 255, 0)); //3、进行判断 float fScale = (float)boundRect.width / (float)filmSize[i].width; if (fScale >= 0.65 && boundRect.x>=8) { putText(draw, "Y5", cv::Point(filmLocation[i].x + 150, filmLocation[i].y), FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(255, 0, 0), 7); } else { putText(draw, "N5", cv::Point(filmLocation[i].x + 150, filmLocation[i].y), FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(0, 0, 255), 7); }6、小螺丝 采用的是阈值、投影的方法进行解决。所不同的这里是竖向投影。
littleScrewMat[i] = src(Rect(LittleScrewLocation[i], LittleScrewSize[i])); cvtColor(littleScrewMat[i], tmpLittleMat[i], COLOR_BGR2GRAY); threshold(tmpLittleMat[i], tmpLittleMat[i], 100, 255, THRESH_BINARY); //取一列的投影 Mat tmp = tmpLittleMat[i].col(tmpLittleMat[i].cols * 0.7); int iLittleScrewMat = 0; for (int i = 0; i < tmp.rows - 2; i++) { if (0 == tmp.at<uchar>(i, 0) && tmp.at<uchar>(i + 1, 0) > 0 && tmp.at<uchar>(i + 2, 0) > 0) iLittleScrewMat++; } if (iLittleScrewMat <= 0)//超级参数 { putText(draw, "N6", cv::Point(LittleScrewLocation[i].x + 150, LittleScrewLocation[i].y), FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(0, 0, 255), 7); } else { …… }7、小螺丝红漆 采用的是颜色(红色)分析的方法,然后做了一些面积比对。这个思路的来源是“你的名字”
littleScrewMat[i] = src(Rect(LittleScrewLocation[i], LittleScrewSize[i])); cvtColor(littleScrewMat[i], tmpLittleMat[i], COLOR_BGR2GRAY); threshold(tmpLittleMat[i], tmpLittleMat[i], 100, 255, THRESH_BINARY); //取一列的投影 Mat tmp = tmpLittleMat[i].col(tmpLittleMat[i].cols * 0.7); int iLittleScrewMat = 0; for (int i = 0; i < tmp.rows - 2; i++) { if (0 == tmp.at<uchar>(i, 0) && tmp.at<uchar>(i + 1, 0) > 0 && tmp.at<uchar>(i + 2, 0) > 0) iLittleScrewMat++; } if (iLittleScrewMat <= 0)//超级参数 { putText(draw, "N6", cv::Point(LittleScrewLocation[i].x + 150, LittleScrewLocation[i].y), FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(0, 0, 255), 7); } else { …… }
8、黑色隔弧板 采用的是阈值、投影的方法进行解决。
bbMat[i] = src(Rect(blackBoardLocation[i], blackBoardSize[i])); cvtColor(bbMat[i], bbTmpMat[i], COLOR_BGR2GRAY); threshold(bbTmpMat[i], bbTmpMat[i], 100, 255, cv::THRESH_OTSU); bitwise_not(bbTmpMat[i], bbTmpMat[i]); //1、投影分析 Mat tmp = bbTmpMat[i].row(bbTmpMat[i].rows/2); int ibbMat = 0; for (int i = 0; i < tmp.cols - 2; i++) { if (0 == tmp.at<uchar>(0,i) && tmp.at<uchar>(0,i + 1) > 0 && tmp.at<uchar>(0,i + 2) > 0) ibbMat++; }9、右侧大螺丝 同问题1。 10、灭弧室板 采用的是阈值、轮廓(数量)分析的方法解决,再辅助投影分析。
cutMat[icutMat] = src(Rect(cutLocation[icutMat], cutSize[icutMat])); cvtColor(cutMat[icutMat], cutTmpMat[icutMat], COLOR_BGR2GRAY); threshold(cutTmpMat[icutMat], cutTmpMat[icutMat], 100, 255, cv::THRESH_OTSU); bitwise_not(cutTmpMat[icutMat], cutTmpMat[icutMat]); vector<VP> vpTmpMat= connection2(cutTmpMat[icutMat]); //printf("vp size is%d\n", vpTmpMat.size()); //补充一个投影 tmpCutRow[icutMat] = cutTmpMat[icutMat].row(cutTmpMat[icutMat].rows * 0.7); int iCut = 0; for (int icutTmpMat = 0; icutTmpMat < tmpCutRow[icutMat].cols - 1; icutTmpMat++) { if ( tmpCutRow[icutMat].at<uchar>(0, icutTmpMat)>0 && 0 == tmpCutRow[icutMat].at<uchar>(0, icutTmpMat + 1) ) iCut++; } if (vpTmpMat.size() < 11 && iCut < 3) { putText(draw, "N10", cv::Point(cutLocation[icutMat].x + 150, cutLocation[icutMat].y), FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(0, 0, 255), 7); } else { putText(draw, "Y10", cv::Point(cutLocation[icutMat].x + 150, cutLocation[icutMat].y), FONT_HERSHEY_SIMPLEX, 3.0f, CV_RGB(255, 0, 0), 7); }
以上是检测类项目中的常用方法
在这种“量化”的项目中,越是简单的算法越能够得出稳定有效的结论;标签:tmp,int,检测,算法,icutMat,&&,视觉,cv,255 From: https://www.cnblogs.com/jsxyhelu/p/16997985.html