Blob是指图像中的一块连通区域,Blob分析就是对前景/背景分离后的二值图像,进行连通域提取和标记。知识点就是SimpleBlobDetector的使用,blob(斑点)筛选条件:斑点颜色、面积、圆度、惯性率、凸度。

void blobDetector()
{
	Mat img = imread("d:\\11.jpg");

	SimpleBlobDetector::Params params;
    params.thresholdStep = 10;    //二值化的阈值步长
	params.minThreshold = 20;
	params.maxThreshold = 200;
	params.filterByArea = true;
	params.minArea = 10;
	params.maxArea = 80000;
	params.filterByCircularity = true;
	params.minCircularity = 0.5;

	Ptr<SimpleBlobDetector> detector = SimpleBlobDetector::create(params);
	vector<KeyPoint> key_points;
	detector->detect(img, key_points);
	Mat output_img;
	drawKeypoints(img, key_points, output_img, Scalar(0, 0, 255), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

	namedWindow("SimpleBlobDetector", WINDOW_NORMAL);
	imshow("SimpleBlobDetector", output_img);
	waitKey(0);
}
  • 阈值:通过使用以minThreshold开始的阈值对源图像进行阈值处理,将源图像转换为多个二进制图像。这些阈值以thresholdStep递增,直到maxThreshold。因此,第一个阈值为minThreshold,第二个阈值为minThreshold + thresholdStep,第三个阈值为minThreshold + 2 x thresholdStep,依此类推;
  • 按大小:可以通过设置参数filterByArea = 1以及minArea和maxArea的适当值来基于大小过滤blob。例如。设置minArea = 100将滤除所有少于100个像素的斑点。
  • 按圆度:这只是测量斑点距圆的距离。例如。正六边形的圆度比正方形高。要按圆度过滤,请设置filterByCircularity =1。然后为minCircularity和maxCircularity设置适当的值。圆度定义为(

opencv 找螺纹起始点 opencv精确找圆_权值

  • opencv 找螺纹起始点 opencv精确找圆_二值图像_02

以下给出 opencv 对候选 Blob 筛查的部分源码:

https://github.com/opencv/opencv/blob/4.2.0/modules/features2d/src/feature2d.cpp

https://github.com/opencv/opencv/blob/4.2.0/modules/features2d/src/blobdetector.cpp

SimplerBlobDetector/SimplerBlobDetector.cpp at master · joostvanstuijvenberg/SimplerBlobDetector · GitHub

//image为输入的灰度图像
//binaryImage为二值图像
//centers表示该二值图像的斑点
void SimpleBlobDetector::findBlobs(const cv::Mat &image, const cv::Mat &binaryImage, vector<Center> ¢ers) const
{
    (void)image;
    centers.clear();    //斑点变量清零
 
    vector < vector<Point> > contours;    //定义二值图像的斑点的边界像素变量
    Mat tmpBinaryImage = binaryImage.clone();    //复制二值图像
    //调用findContours函数,找到当前二值图像的所有斑点的边界
    findContours(tmpBinaryImage, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
 
#ifdef DEBUG_BLOB_DETECTOR
    //  Mat keypointsImage;
    //  cvtColor( binaryImage, keypointsImage, CV_GRAY2RGB );
    //
    //  Mat contoursImage;
    //  cvtColor( binaryImage, contoursImage, CV_GRAY2RGB );
    //  drawContours( contoursImage, contours, -1, Scalar(0,255,0) );
    //  imshow("contours", contoursImage );
#endif
    //遍历当前二值图像的所有斑点
    for (size_t contourIdx = 0; contourIdx < contours.size(); contourIdx++)
    {
        //结构类型Center代表着斑点,它包括斑点的中心位置,半径和权值
        Center center;    //斑点变量
        //初始化斑点中心的置信度,也就是该斑点的权值
        center.confidence = 1;
        //调用moments函数,得到当前斑点的矩
        Moments moms = moments(Mat(contours[contourIdx]));
        if (params.filterByArea)    //斑点面积的限制
        {
            double area = moms.m00;    //零阶矩即为二值图像的面积
            //如果面积超出了设定的范围,则不再考虑该斑点
            if (area < params.minArea || area >= params.maxArea)
                continue;
        }
 
        if (params.filterByCircularity)    //斑点圆度的限制
        {
            double area = moms.m00;    //得到斑点的面积
            //计算斑点的周长
            double perimeter = arcLength(Mat(contours[contourIdx]), true);
            //由公式3得到斑点的圆度
            double ratio = 4 * CV_PI * area / (perimeter * perimeter);
            //如果圆度超出了设定的范围,则不再考虑该斑点
            if (ratio < params.minCircularity || ratio >= params.maxCircularity)
                continue;
        }
 
        if (params.filterByInertia)    //斑点惯性率的限制
        {
            //计算公式13中最右侧等式中的开根号的值
            double denominator = sqrt(pow(2 * moms.mu11, 2) + pow(moms.mu20 - moms.mu02, 2));
            const double eps = 1e-2;    //定义一个极小值
            double ratio;
            if (denominator > eps)
            {
                //cosmin和sinmin用于计算图像协方差矩阵中较小的那个特征值λ2
                double cosmin = (moms.mu20 - moms.mu02) / denominator;
                double sinmin = 2 * moms.mu11 / denominator;
                //cosmin和sinmin用于计算图像协方差矩阵中较大的那个特征值λ1
                double cosmax = -cosmin;
                double sinmax = -sinmin;
                //imin为λ2乘以零阶中心矩μ00
                double imin = 0.5 * (moms.mu20 + moms.mu02) - 0.5 * (moms.mu20 - moms.mu02) * cosmin - moms.mu11 * sinmin;
                //imax为λ1乘以零阶中心矩μ00
                double imax = 0.5 * (moms.mu20 + moms.mu02) - 0.5 * (moms.mu20 - moms.mu02) * cosmax - moms.mu11 * sinmax;
                ratio = imin / imax;    //得到斑点的惯性率
            }
            else
            {
                ratio = 1;    //直接设置为1,即为圆
            }
            //如果惯性率超出了设定的范围,则不再考虑该斑点
            if (ratio < params.minInertiaRatio || ratio >= params.maxInertiaRatio)
                continue;
            //斑点中心的权值定义为惯性率的平方
            center.confidence = ratio * ratio;
        }
 
        if (params.filterByConvexity)    //斑点凸度的限制
        {
            vector < Point > hull;    //定义凸壳变量
            //调用convexHull函数,得到该斑点的凸壳
            convexHull(Mat(contours[contourIdx]), hull);
            //分别得到斑点和凸壳的面积,contourArea函数本质上也是求图像的零阶矩
            double area = contourArea(Mat(contours[contourIdx]));
            double hullArea = contourArea(Mat(hull));
            double ratio = area / hullArea;    //公式5,计算斑点的凸度
            //如果凸度超出了设定的范围,则不再考虑该斑点
            if (ratio < params.minConvexity || ratio >= params.maxConvexity)
                continue;
        }
 
        //根据公式7,计算斑点的质心
        center.location = Point2d(moms.m10 / moms.m00, moms.m01 / moms.m00);
 
        if (params.filterByColor)    //斑点颜色的限制
        {
            //判断一下斑点的颜色是否正确
            if (binaryImage.at<uchar> (cvRound(center.location.y), cvRound(center.location.x)) != params.blobColor)
                continue;
        }
 
        //compute blob radius
        {
            vector<double> dists;    //定义距离队列
            //遍历该斑点边界上的所有像素
            for (size_t pointIdx = 0; pointIdx < contours[contourIdx].size(); pointIdx++)
            {
                Point2d pt = contours[contourIdx][pointIdx];    //得到边界像素坐标
                //计算该点坐标与斑点中心的距离,并放入距离队列中
                dists.push_back(norm(center.location - pt));
            }
            std::sort(dists.begin(), dists.end());    //距离队列排序
            //计算斑点的半径,它等于距离队列中中间两个距离的平均值
            center.radius = (dists[(dists.size() - 1) / 2] + dists[dists.size() / 2]) / 2.;
        }
 
        centers.push_back(center);    //把center变量压入centers队列中
 
#ifdef DEBUG_BLOB_DETECTOR
        //    circle( keypointsImage, center.location, 1, Scalar(0,0,255), 1 );
#endif
    }
#ifdef DEBUG_BLOB_DETECTOR
    //  imshow("bk", keypointsImage );
    //  waitKey();
#endif
}

---

参考文献

OpenCV图像处理中“找圆技术”的使用