c++ - 通过迭代自适应阈值和形状分析检测圆形对象的集群

标签 c++ algorithm image-processing opencv openmp

我一直在开发一个应用程序来计算圆形物体,例如图片中的细菌菌落。

物体通常与背景完全不同,这一点很容易做到这一点。

然而,很少有困难使分析变得棘手:

  1. 背景会呈现渐变和快速的强度变化。
  2. 在容器的边缘,对象将是椭圆形而不是圆形。
  3. 物体的边缘有时会比较模糊。
  4. 对象将聚集在一起。
  5. 对象可以非常小(直径 6 像素)
  6. 最终,算法将被那些对图像分析没有深入了解的人使用(通过 GUI),因此参数必须直观且很少。

该问题已在科学文献中多次解决并“解决”,例如使用循环霍夫变换或分水岭方法,但我从未对结果感到满意。

所描述的一种简单方法是通过自适应阈值处理获得前景,并使用距离变换分割(如我在 this post 中所述)聚类对象。

我已经成功地实现了这种方法,但它不能总是处理强度的突然变化。此外,同行要求我提出一种更“新颖”的方法。

因此我一直在寻找一种提取前景的新方法。

因此,我研究了其他阈值/ Blob 检测方法。 我尝试了 MSER,但发现在我的情况下它们不是很健壮而且速度很慢。

我最终提出了一个算法,到目前为止,它给了我很好的结果:

  1. 我拆分图像的三个 channel 并减少它们的噪点(模糊/中值模糊)。对于每个 channel :
  2. 我通过计算原始 channel 和卷积(通过大内核模糊) channel 之间的绝对差来应用自适应阈值的第一步的手动实现。然后,对于阈值的所有相关值:
  3. 我对 2) 的结果应用阈值
  4. 寻找轮廓
  5. 在授予其形状(大小、面积、凸度...)时验证或使轮廓无效
  6. 只有有效的连续区域(由轮廓分隔)然后在累加器中重绘(每个 channel 1 个累加器)。
  7. 在累积超过阈值的连续区域后,我最终得到了一张“区域分数”的 map 。强度最高的区域是最常满足形态过滤标准的区域。
  8. 然后将三张 map (每个 channel 一张)转换为灰度图并设置阈值(阈值由用户控制)

只是为了向您展示我必须使用的图像类型: enter image description here 这张图片代表了顶部 3 个示例图像的一部分,以及底部各个部分的算法结果(蓝色 = 前景)。

这是我的 C++ 实现:3-7

/*
 * cv::Mat dst[3] is the result of the absolute difference between original and convolved channel.
 * MCF(std::vector<cv::Point>, int, int) is a filter function that returns an positive int only if the input contour is valid.
 */

/* Allocate 3 matrices (1 per channel)*/
cv::Mat accu[3];

/* We define the maximal threshold to be tried as half of the absolute maximal value in each channel*/
int maxBGR[3];
for(unsigned int i=0; i<3;i++){
    double min, max;
    cv::minMaxLoc(dst[i],&min,&max);
    maxBGR[i] = max/2;
    /* In addition, we fill accumulators by zeros*/
    accu[i]=cv::Mat(compos[0].rows,compos[0].cols,CV_8U,cv::Scalar(0));
}
/* This loops are intended to be multithreaded using
#pragma omp parallel for collapse(2) schedule(dynamic)
For each channel */
for(unsigned int i=0; i<3;i++){
    /* For each value of threshold (m_step can be > 1 in order to save time)*/
    for(int j=0;j<maxBGR[i] ;j += m_step ){
            /* Temporary matrix*/
            cv::Mat tmp;
            std::vector<std::vector<cv::Point> > contours;
            /* Thresholds dst by j*/
            cv::threshold(dst[i],tmp, j, 255, cv::THRESH_BINARY);
            /* Finds continous regions*/
            cv::findContours(tmp, contours, CV_RETR_LIST, CV_CHAIN_APPROX_TC89_L1);
            if(contours.size() > 0){
                /* Tests each contours*/
                for(unsigned int k=0;k<contours.size();k++){
                    int valid = MCF(contours[k],m_minRad,m_maxRad);
                    if(valid>0){
                        /* I found that redrawing was very much faster if the given contour was copied in a smaller container.
                         * I do not really understand why though. For instance,
                         cv::drawContours(miniTmp,contours,k,cv::Scalar(1),-1,8,cv::noArray(), INT_MAX, cv::Point(-rect.x,-rect.y));
                         is slower especially if contours is very long.
                         */
                        std::vector<std::vector<cv::Point> > tpv(1);
                        std::copy(contours.begin()+k, contours.begin()+k+1, tpv.begin());
                        /* We make a Roi here*/
                        cv::Rect rect = cv::boundingRect(tpv[0]);
                        cv::Mat miniTmp(rect.height,rect.width,CV_8U,cv::Scalar(0));
                        cv::drawContours(miniTmp,tpv,0,cv::Scalar(1),-1,8,cv::noArray(), INT_MAX, cv::Point(-rect.x,-rect.y));
                        accu[i](rect)    = miniTmp + accu[i](rect);
                    }
                }
            }
        }
    }
/* Make the global scoreMap*/
cv::merge(accu,3,scoreMap);
/* Conditional noise removal*/
if(m_minRad>2)
    cv::medianBlur(scoreMap,scoreMap,3);
cvtColor(scoreMap,scoreMap,CV_BGR2GRAY);

我有两个问题:

  1. 这种前景提取方法的名称是什么?您认为在这种情况下使用它可能不合适吗?

  2. 由于递归查找和绘制轮廓非常密集,我想让我的算法更快。你能告诉我实现这个目标的任何方法吗?

非常感谢您的帮助,

最佳答案

几年前,我写了一个应用程序来检测显微镜图像中的细胞。代码是用 Matlab 编写的,我认为现在比它应该的要复杂得多(这是我的第一个 CV 项目),所以我只会概述对你有帮助的技巧。顺便说一句,它的速度非常慢,但它非常擅长分离大量的双胞胎。

我定义了一个度量来评估给定点是单元格中心的机会: - 亮度在其周围呈圆形下降 - 纹理亮度的变化遵循给定的模式 - 一个单元格不会覆盖超过 % 的相邻单元格

有了它,我开始反复寻找最佳单元格,将其标记为已找到,然后寻找下一个。因为这样的搜索很昂贵,所以我使用遗传算法在我的特征空间中更快地搜索。

部分结果如下:

Cells 2 Cells

关于c++ - 通过迭代自适应阈值和形状分析检测圆形对象的集群,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11631188/

相关文章:

c++ - 如何使用类型名?

c++ - 计算中常用值的预定义 - 它会改变什么吗?

c++ - 为什么 std::basic_ostream 同时具有自由和成员运算符<<?

java - 使用深度优先搜索在树中查找节点

c++ - 有效地计算多维数组的累加和?

python - 即使图像在 Python 中的 OpenCV 中包含多条线,霍夫线变换也仅识别一条线

c++ - 在 Visual Studio 2008 中使用 mmsystem.h

algorithm - 使用 AND 和 OR 转换逻辑条件

python-3.x - 在不使用内置函数的情况下使用 python 和 opencv 进行直方图均衡

image-processing - 如何转换图像?