初衷
最近比较闲,跟同学聊天讲到他的课题:医学图像分割,提取左心室区域。
我就好奇要了原始图片,发现超声图像果然比红外图像分辨率低,他指给我左心室所在区域。
思路
拿到这张图第一眼,脑海里蹦出无数个小想法:
- 感兴趣区域为一个扇形,所以首先制作掩模;
- 这种灰度分级模糊的图像,若想分类,可以试试Kmeans,或者直接用阈值分割;
- 后续可以分析特征,通过边界跟踪一类得到。
具体流程
- 1、原图通过k_means二分类,并得到二值分割图;
- 2、形态学处理将粘连区域拆分,去除连通区域内部空洞;
- 3、掩模后只保留扇形内数据;
- 4、提取轮廓并以面积为评估对象做筛选;
- 5、欧式距离为评估对象,后续维护好目标跟踪。(在后续分析视频序列时,发现其实目标位置基本不会大的变化,只是在形状发生变化,因此可以借助欧式距离跟踪)
制作扇形掩模
查找opencv并没有现成的绘制扇形函数,只有手动绘制了。
大概思路为:
- 1、绘制扇形的圆弧;
- 2、绘制两条半径,绘制半径需要知道角点,所以牵扯些许三角函数的计算。
尝试参数后,代码如下:
//生成扇形轮廓,角度
void get_fans(Mat &img,cv::Point center,double angle, int radius)
{
ellipse(img, center, cv::Size(radius, radius), 0, 90 - angle / 2, 90 + angle / 2, Scalar(255, 255, 255), 3, 8);
//计算扇形顶点坐标,先计算弦长和高
int chord_len = radius*sin(angle*3.14 / (2 * 180));
int height = radius*cos(angle*3.14 / (2 * 180));
//一左一右,中心点加偏移量即为顶点坐标
cv::Point corner_1 = center + cv::Point(-chord_len, height);
cv::Point corner_2 = center + cv::Point(chord_len, height);
line(img, center, corner_1, Scalar(255, 255, 255), 3, 8);
line(img, center, corner_2, Scalar(255, 255, 255), 3, 8);
}
朋友只需要根据参数调整,就能得到他们项目所需要的掩模。这个程序只是生成一张扇形的轮廓,生成掩模,还需要提取轮廓重新绘制,并将内部填充。
这部分代码如下:
get_fans(maskImage, cv::Point(520, 0), 77.4, 806);
vector<vector<cv::Point> > contours;//查找轮廓
vector<cv::Vec4i> hierarchy;//轮廓需要
findContours(maskImage, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
maskImage = cv::Mat::zeros(cv::Size(srcImage.cols, srcImage.rows), CV_8UC1);
//绘制自定义掩模,CV_FILLED表示完全填充内部
drawContours(maskImage, contours, -1, cv::Scalar::all(255), CV_FILLED);
完整代码就不分享了,借用Kmeans在分类时存在需要不定时反色情形,在对视频序列处理时,直接阈值化后效果也不错。
Kmeans效果还是有那么点的: