我一直在开发一个应用程序来计算圆形物体,例如图片中的细菌菌落。
让这一切变得简单的是,物体通常与背景有很大区别。
然而,有一些困难使得分析变得棘手:
- 背景将呈现渐变以及快速的强度变化。
- 在容器的边缘,对象将是椭圆形而不是圆形。
- 物体的边缘有时相当模糊。
- 对象将聚集。
- 对象可以非常小(直径 6 像素)
- 最终,这些算法将由对图像分析没有深入了解的人使用(通过 GUI),因此参数必须直观且很少。
这个问题已在科学文献中多次提及并“解决”,例如使用循环霍夫变换或分水岭方法,但我从未对结果感到满意。
所描述的一种简单方法是通过自适应阈值和分割来获取前景(正如我在这个帖子 https://stackoverflow.com/questions/10313602/reshaping-noisy-coin-into-a-circle-form/10324759#10324759)使用距离变换的聚类对象。
我已经成功地实现了这种方法,但它并不总是能够应对强度的突然变化。另外,同行们要求我提出一种更“新颖”的方法。
因此,我正在寻找一种提取前景的新方法。
因此,我研究了其他阈值/斑点检测方法。
我尝试了 MSER,但发现它们不是很强大,而且对我来说速度很慢。
我最终想出了一个算法,到目前为止,它给了我很好的结果:
- 我分割图像的三个通道并减少它们的噪声(模糊/中值模糊)。对于每个通道:
- 我通过计算原始通道和卷积通道(通过大内核模糊)之间的绝对差来手动实现自适应阈值处理的第一步。然后,对于所有相关的阈值:
- 我对 2) 的结果应用阈值
- 找到轮廓
- 根据轮廓的形状(大小、面积、凸度...)验证或无效轮廓
- 仅有效的连续区域(i.e.然后在累加器中重新绘制(每个通道 1 个累加器)。
- 在累积超过阈值的连续区域后,我最终得到了“区域分数”的地图。具有最高强度的区域是最常满足形态过滤标准的区域。
- 然后将三个图(每个通道一个)转换为灰度并设置阈值(阈值由用户控制)
Just to show you the kind of image I have to work with:
This picture represents part of 3 sample images in the top and the result of my algorithm (blue = foreground) of the respective parts in the bottom.
这是我的 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);
我有两个问题:
这种前景提取方法的名称是什么?您是否认为在这种情况下使用它可能不合适?
由于递归查找和绘制轮廓非常密集,因此我想让我的算法更快。你能告诉我有什么方法可以实现这个目标吗?
非常感谢您的帮助,