opencv实战——寻找缺失和靶心

引言

记录两个基于二值图像分析的较为经典的例子,希望能够得到更多的启发,从而想到更好的解决类似问题的思路。

?01


问题一:寻找靶心

opencv实战——寻找缺失和靶心

仔细观察上图,可以看到两个最直接的是靶心有十字交叉线,而在OpenCV形态学处理中,支持十字交叉结构元素,所以我们可以先检测两条线,然后获取十字交叉结构,最后对结构进行轮廓分析,获取中心点,即可获得最终的靶心位置,最终寻找到的靶心位置。

opencv实现:

Mat src = imread("D:/opencv练习图片/寻找靶心.jpg");
    imshow("原图", src);
    Mat gray,binary,hline,vline;
    cvtColor(src, gray, COLOR_RGB2GRAY);
    //二值化
    threshold(gray, binary, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
    imshow("二值化", binary);
    //形态学处理
    Mat kernel1 = getStructuringElement(MORPH_RECT, Size(70, 1), Point(-1, -1));
    Mat kernel2 = getStructuringElement(MORPH_RECT, Size(1, 50), Point(-1, -1));
    morphologyEx(binary, hline, MORPH_OPEN, kernel1, Point(-1, -1));
    morphologyEx(binary, vline, MORPH_OPEN, kernel2, Point(-1, -1));
    vector<vector<Point>> contours;
    findContours(hline, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point());
    Mat mask = Mat::zeros(hline.size(), CV_8U);
    int max = 0;
    int index = 0;
    for (int i = 0; i < contours.size(); i++)
    {
        double area = contourArea(contours[i]);
        if (area > max)
        {
            max = area;
            index = i;
            cout << index << endl;
            drawContours(mask, contours, index, Scalar(255, 255, 255), -1, 8);
        }
    }
    imshow("h", mask);
    vector<vector<Point>> contours1;
    findContours(vline, contours1, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point());
    int max1 = 0;
    int index1 = 0;
    for (int i = 0; i < contours1.size(); i++)
    {
        double area = contourArea(contours1[i]);
        if (area > max1)
        {
            max1 = area;
            index1 = i;
            drawContours(mask, contours1, index1, Scalar(255, 255, 255), -1, 8);
        }
    }
    imshow("提取十字", mask);
    Mat kernel3 = getStructuringElement(MORPH_CROSS, Size(13, 13), Point(-1, -1));
    morphologyEx(mask, mask, MORPH_OPEN, kernel3, Point(-1, -1));
    imshow("交点", mask);
    vector<vector<Point>> contours3;
    findContours(mask, contours3, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());
    RotatedRect rect = minAreaRect(contours3[0]);
    Point center = rect.center;
    circle(src, center, 4, Scalar(0, 0, 255), 1, 8);
    imshow("结果", src);

opencv实战——寻找缺失和靶心opencv实战——寻找缺失和靶心

?02


*问题二:*计数并寻找其中的缺失点

opencv实战——寻找缺失和靶心

仔细分析图像发现,中间都毫无另外的有个白色很亮的圆圈。

因此我们可以通过二值图像分析来提取 + 轮廓分析来提取到这些点,得到这些轮廓点之后通过分析整个轮廓区域得到倾斜角度,进行纠偏,然后通过X与Y投影进行分割,得到每个零件的中心位置坐标,根据每一行的间隔设置阈值,从而实现缺少部分部分的标出与件数统计

opencv分析:


(一)读入图像,预处理(形态学梯度,二值化)


Mat srcImage = imread("D:/opencv练习图片/工件计数.jpg");
    namedWindow("原始图");
    imshow("原始图", srcImage);
    Mat grayImage;
    cvtColor(srcImage, grayImage, COLOR_RGB2GRAY);
    Mat kernal = getStructuringElement(MORPH_RECT, Size(3, 3));
    Mat gradientImage;
    morphologyEx(grayImage, gradientImage, MORPH_GRADIENT, kernal);
    namedWindow("gradientImage形态学梯度");
    imshow("gradientImage形态学梯度", gradientImage);
    Mat thresholdImage;
    threshold(gradientImage, thresholdImage, 0, 255, THRESH_OTSU);
    namedWindow("二值化OTSU图");
    imshow("二值化OTSU图", thresholdImage);

opencv实战——寻找缺失和靶心opencv实战——寻找缺失和靶心


(二)再次形态学(保留圆形区域)


kernal = getStructuringElement(MORPH_ELLIPSE, Size(5, 5));
    Mat openImage, closeImage;
    morphologyEx(thresholdImage, openImage, MORPH_OPEN, kernal);
    imshow("openImage", openImage);
    kernal = getStructuringElement(MORPH_ELLIPSE, Size(10, 10));
    morphologyEx(openImage, closeImage, MORPH_CLOSE, kernal);
    imshow("closeImage", closeImage);

开运算:分割区域闭运算:填充孔洞

opencv实战——寻找缺失和靶心opencv实战——寻找缺失和靶心


(三)寻找轮廓并筛选,最后提取点


vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;
    findContours(closeImage, contours, hierarchy, RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
    Mat resultImage = Mat::zeros(grayImage.rows, grayImage.cols, grayImage.type());
    int total = 0;//计数值
    for (int i = 0; i < contours.size(); i++)
    {
        double area = contourArea(contours[i]);
        if (area < 55) continue;//面积大于55的继续执行

        total++;//计算有多少面积大于55的个数(工件数)
        RotatedRect rect = minAreaRect(contours[i]);
        circle(resultImage, rect.center, 5, Scalar(255), -1);//用圆显示
    }
    imshow("提取点", resultImage);

opencv实战——寻找缺失和靶心


(四)对点区域求最小外接矩形,计算角度并矫正


vector<Point> pts;
    for (int i = 0; i < resultImage.rows; i++)
    {
        for (int j = 0; j < resultImage.cols; j++)
        {
            if (resultImage.ptr<uchar>(i)[j] == 255)
            {
                pts.push_back(Point(j, i));
            }
        }
    }
    RotatedRect rect = minAreaRect(pts);
    Point2f rectVertex[4];
    rect.points(rectVertex);

    for (int i = 0; i < 4; i++)
    {
        putText(resultImage, to_string(i), rectVertex[i], FONT_HERSHEY_SIMPLEX, 1.0, Scalar(200));
        line(resultImage, rectVertex[i], rectVertex[(i + 1) % 4], Scalar(100), 2, 8);
    }
    cout << "角度为:" << rect.angle;
    cout << "宽度为:" << rect.size.width;
    cout << "高度为:" << rect.size.height;
    namedWindow("resultImage");
    imshow("最小外接矩形", resultImage);
    float angle = rect.angle - 90;
    Point center = rect.center;
    //计算旋转后的画布大小,并将旋转中心平移到新的旋转中心
    Rect bbox = RotatedRect(center, Size(srcImage.cols, srcImage.rows), angle).boundingRect();
    Mat matrix = getRotationMatrix2D(rect.center, angle, 1);
    matrix.at<double>(0, 2) += bbox.width / 2.0 - center.x;
    matrix.at<double>(1, 2) += bbox.height / 2.0 - center.y;    
    warpAffine(resultImage, resultImage, matrix, resultImage.size());
    warpAffine(srcImage, srcImage, matrix, srcImage.size());
    imshow("矫正原图", srcImage);
    imshow("矫正二值图", resultImage);

opencv实战——寻找缺失和靶心opencv实战——寻找缺失和靶心

? 注意:

再通过RotatedRect类获得的矩形角度angle,在计算旋转矩阵时,应该再减去90度,才是旋转角度。


(五)构造查找近邻函数Y_projection()


void Y_projection(Mat &warp, Mat &src, int max_gap, int &first, int &end)
{

    vector<int> y_bins;
    for (int i = 0; i < warp.cols; i++) {
        int found_y = 0;
        for (int j = first; j < end; j++) {

            if (warp.at<uchar>(j, i) == 255) {

                found_y += 1;
            }
        }
        if (found_y > 0) {
            y_bins.push_back(i);
        }
    }

    vector<int> y_tbins;
    for (int i = 0; i < y_bins.size() - 1; i++)
    {
        int gap = y_bins[i + 1] - y_bins[i];
        if (gap >= 15) {
            y_tbins.push_back(y_bins[i + 1] - (gap / 2));
        }
        if (gap >= 50) {
            circle(src, Point(y_bins[i + 1] - (gap / 2), (end - (end - first) / 2)), 5, Scalar(0, 255, 255), -1);
        }
    }

}

(六)缺失排查,并显示结果


//计算每行工具所在的行数
    vector<int> bins;
    vector<int> tbins;
    for (int i = 0; i < resultImage.rows; i++) {
        int found = 0;
        for (int j = 0; j < resultImage.cols; j++) {

            if (resultImage.at<uchar>(i, j) == 255) {

                found += 1;
            }
        }
        if (found > 0) {
            bins.push_back(i);
        }
    }

    for (int i = 0; i < (bins.size() - 1); i++) {

        int gap = bins[i + 1] - bins[i];
        if (gap >= 15) {
            cout << "tbins: " << bins[i + 1] - (gap / 2) << endl;
            tbins.push_back(bins[i + 1] - (gap / 2));
        }
    }
    //逐行排查缺失工具的位置
    Mat dstImage;
    srcImage.copyTo(dstImage);
    int h = resultImage.rows - 1;
    for (int i = 0; i < tbins.size(); i++) {
        if (i == 0)
        {
            //第一排工具所占行数从0到第一个位置
            Y_projection(resultImage, dstImage, 50, i, tbins[i]);
        }
        else if (i == (tbins.size() - 1))
        {
            //最后一排工具所占行数从最后一个位置到图片最后一行
            Y_projection(resultImage, dstImage, 50, tbins[i], h);
        }
        else
        {
            //中间行工具所占行数为上下两个位置之间
            int end = tbins[i] - 1;
            Y_projection(resultImage, dstImage, 50, tbins[i - 1], end);
        }
    }
    putText(dstImage, "numbers: " + to_string(total), Point(50, 50), FONT_HERSHEY_SIMPLEX, 1.0, Scalar(0, 0, 255));
    imshow("dstImage", dstImage);
    waitKey(0);

opencv实战——寻找缺失和靶心

摘录于:Opencv学堂(自己用c++实现)

原文链接: https://www.cnblogs.com/xyf327/p/14809590.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/210915

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月13日 上午12:31
下一篇 2023年2月13日 上午12:32

相关推荐