针对重叠的图像分割是非常具有挑战的,正好之前一起工作过的同事跟我一起讨论了关于这方面的问题,我首先想到的就是用分水岭来进行分割。
下面我将结合他提供的细胞图像来实现分水岭分割算法的实现。
1、RGB2HSV空间变换
首先我们观察图像可以看到背景是白色,而我们期望的目标颜色有深有浅,而且颜色也不一样,所以直接将RGB转成HSV,分割出白色区域然后再取反即可。关于白色在HSV中范围,我之前的文章中有分享过,这里就不给出来了。代码实现:
cv::Mat hsvimage, thresholdimage;
cv::cvtColor(src, hsvimage, CV_BGR2HSV);
cv::inRange(hsvimage, cv::Scalar(0, 0, 221), cv::Scalar(180, 30, 255), thresholdimage);
result = ~thresholdimage.clone();
2、形态学闭操作
因为分割的图像中间会有一些空洞,我们用形态学闭操作来弥补这个缺陷。
代码实现:
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(kernelsize, kernelsize));
cv::morphologyEx(src, result, cv::MORPH_CLOSE, element, cv::Point(-1, -1));
3、生成标记区域
首先要生成前景区域,代码实现:
cv::distanceTransform(src, dist, CV_DIST_L2, 5);
cv::normalize(dist, dist, 0, 255, cv::NORM_MINMAX);
double minv = 0.0, maxv = 0.0;
cv::minMaxIdx(dist, &minv, &maxv);
cv::Mat sure_fg;
cv::threshold(dist, sure_fg, 0.4*maxv, 255, CV_THRESH_BINARY);
sure_fg.convertTo(sure_fg, CV_8U);
cv::imwrite("sure_fg.jpg", sure_fg);
然后生成背景区域,代码如下:
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(kernelsize, kernelsize));
cv::morphologyEx(src, result, cv::MORPH_DILATE, element, cv::Point(-1, -1));
将上面两个结果相减得到未确定区域。
根据分水岭算法的特点,我们要把未确定区域设置为波谷值(例如0),而之前分割中背景也是0,因此通过连通域分析,我们将背景的值设置成1。代码如下:
cv::Mat labelmatImage = cv::Mat(dist.size(), CV_32SC1);
int label = cv::connectedComponents(sure_fg, labelmatImage, 8);
labelmatImage = labelmatImage 1;
for (int i = 0; i < labelmatImage.rows; i )
{
for (int j = 0; j < labelmatImage.cols; j )
{
if ((int)unkonwn.at<uchar>(i, j) == 255)
{
labelmatImage.at<int>(i, j) = 0;
}
}
}
4、用分水岭算法进行分割
opencv自带的分水岭算法。代码如下:
cv::watershed(src, markers);
最后的效果只能是差强人意吧,大家如果有更好的方法,欢迎一起交流学习。