2016-05-14 45 views
2

3D数据,我的CT肺部图像的3D体积上工作,为了检测结节我需要适应的椭球体模型为每个可疑结节,我怎样才能使一个代码那? 结节是可疑的对象是肿瘤,我的算法需要检查每个对象,并将其近似为椭球,并从椭球参数计算8个特征来构建分类器,通过训练检测是否为结核测试数据,所以需要适合这种椭球椭圆拟合对MATALB

这里的体积CT肺图像

img

这里相同体积的另一片,但它包含了结节的一个切片(黄圈有一个结节),所以我需要我的代码来检查每个形状来确定R使得结节或不

img

+0

+关闭现在你需要更具体。我们不是医疗专业人员,无法知道您的数据的外观以及您想要达到的目标。至少添加样本输入和所需的输出图像/切片,定义结节是什么(通常这个问题的定义会导致单独的解决方案),添加解决方案约束(内存,速度/时间,精度等)任务会过滤掉不需要的数据(只选择感兴趣的对象),然后执行聚类分析并将要点填充到所需的位置。数据的性质决定了拟合的方法(代数,图形,逼近,PCA,..) – Spektre

+0

好吧我添加了一个卷的图片 –

+0

好吧真的非常感谢,我添加了一个图像与结节 –

回答

2

由于我们没有3D数据集在处置我开始2D

所以首先我们需要选择肺部,这样我们就不会再计算其他物体了。由于这是灰度级,我们首先需要以某种方式将其二进制化。我用我自己的class pictureDIP,这将在很大程度上用我growthfill,所以我强烈建议先阅读本:

在那里你会找到你需要这一切的解释。现在,你的任务,我会:

  1. 转RGBA灰度<0,765>

    我只是计算强度i=R+G+B为24位图像通道8位的结果是高达3*255 = 765。由于输入图像是通过JPEG压缩的,因此图像中存在颜色失真和噪声,因此请不要忘记这一点。

  2. 作物出白色边界

    从朝向中间的边界的各外线的中间仅有投射的光线(扫描线)和停止,如果非白色肥胖型像素命中。我用700而不是765阈值来补偿图像中的噪声。现在你得到了可用图像的边界框,以便将其余部分剪出。

  3. 计算直方图

    为了补偿在图像失真将直方图平滑化足以除去所有不需要的噪声和差距。然后从左侧和右侧找到局部最大值(红色)。这将用于二值化treshold(他们绿色之间的中间),这是我的最终结果:

    smoothed histogram

  4. binarise图像

    对**绿色*强度从刚treshold图像直方图。因此,如果i0,i1是直方图中从左到右的局部最大强度,则对阈值(i0+i1)/2。这是结果:

    binary

  5. 删除一切,除了肺部

    这是很容易从外面随便填黑一些预定义的背景颜色。然后以相同的方式所有的白色东西相邻的背景颜色。这将删除人体表面,骨骼,器官和机器,只留下肺部。现在使用一些预定义的Lungs颜色重新着色剩余的黑色。

    lungs

    不应该有黑色留下,其余的白色是可能的结节。

  6. 过程的所有剩余的白色像素

    所以只是循环通过图像和第一白色像素命中洪水与预定义的结节的颜色或不同的对象索引后者使用填充它。我也清楚表面(aqua)和内部(洋红色)。这是结果:

    nodules

    现在你可以为每个结节计算的功能。如果你编写自定义floodfill对于这点,你可以从中获得直接的东西,如:

    • 卷在[pixels]
    • 表面积[pixels]
    • 边框
    • 位置(相对于肺)
    • 重心或质心

    这些都可以用作你的特征变量,并帮助拟合。

  7. 拟合发现面点

    这有很多方法,但我会缓和它尽可能多的我可以提高性能和精度。例如,您可以使用质心作为椭球中心。然后找到它的距离点minmax,并将它们用作半轴(+/-一些正交性修正)。然后只是搜索这些初始值。欲了解更多信息,请参阅:

    你会发现在连接Q/A那儿使用的例子。

子弹的[注释]

所有都适用于3D。在构建自定义floodfill时,请注意递归尾部。太多的信息会真的很快溢出你的堆栈并且也会使事情大大减慢。我如何处理它与一些自定义返回参数这里小例子+ growthfill我用:

//--------------------------------------------------------------------------- 
    void growfill(DWORD c0,DWORD c1,DWORD c2);      // grow/flood fill c0 neigbouring c1 with c2 
    void floodfill(int x,int y,DWORD c);       // flood fill from (x,y) with color c 
    DWORD _floodfill_c0,_floodfill_c1;        // recursion filled color and fill color 
    int _floodfill_x0,_floodfill_x1,_floodfill_n;     // recursion bounding box and filled pixel count 
    int _floodfill_y0,_floodfill_y1; 
    void _floodfill(int x,int y);         // recursion for floodfill 
//--------------------------------------------------------------------------- 
void picture::growfill(DWORD c0,DWORD c1,DWORD c2) 
    { 
    int x,y,e; 
    for (e=1;e;) 
    for (e=0,y=1;y<ys-1;y++) 
     for ( x=1;x<xs-1;x++) 
     if (p[y][x].dd==c0) 
     if ((p[y-1][x].dd==c1) 
      ||(p[y+1][x].dd==c1) 
      ||(p[y][x-1].dd==c1) 
      ||(p[y][x+1].dd==c1)) { e=1; p[y][x].dd=c2; } 
    } 
//--------------------------------------------------------------------------- 
void picture::_floodfill(int x,int y) 
    { 
    if (p[y][x].dd!=_floodfill_c0) return; 
    p[y][x].dd=_floodfill_c1; 
    _floodfill_n++; 
    if (_floodfill_x0>x) _floodfill_x0=x; 
    if (_floodfill_y0>y) _floodfill_y0=y; 
    if (_floodfill_x1<x) _floodfill_x1=x; 
    if (_floodfill_y1<y) _floodfill_y1=y; 
    if (x> 0) _floodfill(x-1,y); 
    if (x<xs-1) _floodfill(x+1,y); 
    if (y> 0) _floodfill(x,y-1); 
    if (y<ys-1) _floodfill(x,y+1); 
    } 
void picture::floodfill(int x,int y,DWORD c) 
    { 
    if ((x<0)||(x>=xs)||(y<0)||(y>=ys)) return; 
    _floodfill_c0=p[y][x].dd; 
    _floodfill_c1=c; 
    _floodfill_n=0; 
    _floodfill_x0=x; 
    _floodfill_y0=y; 
    _floodfill_x1=x; 
    _floodfill_y1=y; 

    _floodfill(x,y); 
    } 
//--------------------------------------------------------------------------- 

这里C++代码,我做了示例图像用:

picture pic0,pic1; 
    // pic0 - source img 
    // pic1 - output img 
int x0,y0,x1,y1,x,y,i,hist[766]; 
color c; 
// copy source image to output 
pic1=pic0; 
pic1.pixel_format(_pf_u);   // grayscale <0,765> 
//     0xAARRGGBB 
const DWORD col_backg=0x00202020; // gray 
const DWORD col_lungs=0x00000040; // blue 
const DWORD col_out =0x0000FFFF; // aqua nodule surface 
const DWORD col_in =0x00800080; // magenta nodule inside 
const DWORD col_test =0x00008040; // green-ish distinct color just for safe recoloring 

// [remove white background] 
// find white background area (by casting rays from middle of border into center of image till non white pixel hit) 
for (x0=0  ,y=pic1.ys>>1;x0<pic1.xs;x0++) if (pic1.p[y][x0].dd<700) break; 
for (x1=pic1.xs-1,y=pic1.ys>>1;x1>  0;x1--) if (pic1.p[y][x1].dd<700) break; 
for (y0=0  ,x=pic1.xs>>1;y0<pic1.ys;y0++) if (pic1.p[y0][x].dd<700) break; 
for (y1=pic1.ys-1,x=pic1.xs>>1;y1>  0;y1--) if (pic1.p[y1][x].dd<700) break; 
// crop it away 
pic1.bmp->Canvas->Draw(-x0,-y0,pic1.bmp); 
pic1.resize(x1-x0+1,y1-y0+1); 

// [prepare data] 
// raw histogram 
for (i=0;i<766;i++) hist[i]=0; 
for (y=0;y<pic1.ys;y++) 
for (x=0;x<pic1.xs;x++)    
    hist[pic1.p[y][x].dd]++; 
// smooth histogram a lot (remove noise and fill gaps due to compression and scanning nature of the image) 
for (x=0;x<100;x++) 
    { 
    for (i=0;i<765;i++) hist[i]=(hist[i]+hist[i+1])>>1; 
    for (i=765;i>0;i--) hist[i]=(hist[i]+hist[i-1])>>1; 
    } 
// find peaks in histogram (for tresholding) 
for (x=0,x0=x,y0=hist[x];x<766;x++) 
    { 
    y=hist[x]; 
    if (y0<y) { x0=x; y0=y; } 
    if (y<y0>>1) break; 
    } 
for (x=765,x1=x,y1=hist[x];x>=0;x--) 
    { 
    y=hist[x]; 
    if (y1<y) { x1=x; y1=y; } 
    if (y<y1>>1) break; 
    } 
// binarize image (tresholding) 
i=(x0+x1)>>1;      // treshold with middle intensity between peeks 
pic1.pf=_pf_rgba;     // result will be RGBA 
for (y=0;y<pic1.ys;y++) 
for (x=0;x<pic1.xs;x++) 
    if (pic1.p[y][x].dd>=i) pic1.p[y][x].dd=0x00FFFFFF; 
    else     pic1.p[y][x].dd=0x00000000; 
pic1.save("out0.png"); 
// recolor outer background 
for (x=0;x<pic1.xs;x++) pic1.p[  0][x].dd=col_backg; // render rectangle along outer border so the filling starts from there 
for (x=0;x<pic1.xs;x++) pic1.p[pic1.ys-1][x].dd=col_backg; 
for (y=0;y<pic1.ys;y++) pic1.p[y][  0].dd=col_backg; 
for (y=0;y<pic1.ys;y++) pic1.p[y][pic1.xs-1].dd=col_backg; 
pic1.growfill(0x00000000,col_backg,col_backg);    // fill its black content outside in 
// recolor white human surface and CT machine 
pic1.growfill(0x00FFFFFF,col_backg,col_backg); 
// recolor Lungs dark matter 
pic1.growfill(0x00000000,col_backg,col_lungs);    // outer border 
pic1.growfill(0x00000000,col_lungs,col_lungs);    // fill its black content outside in 
pic1.save("out1.png"); 
// find/recolor individual nodules 
for (y=0;y<pic1.ys;y++) 
for (x=0;x<pic1.xs;x++) 
    if (pic1.p[y][x].dd==0x00FFFFFF) 
    { 
    pic1.floodfill(x,y,col_test); 
    pic1.growfill(col_lungs,col_test,col_out); 
    pic1.floodfill(x,y,col_in); 
    } 
pic1.save("out2.png"); 

// render histogram 
for (x=0;(x<766)&&(x>>1<pic1.xs);x++) 
for (y=0;(y<=hist[x]>>6)&&(y<pic1.ys);y++) 
    pic1.p[pic1.ys-1-y][x>>1].dd=0x000040FF; 
for (x=x0  ,y=0;(y<=100)&&(y<pic1.ys);y++) pic1.p[pic1.ys-1-y][x>>1].dd=0x00FF0000; 
for (x=x1  ,y=0;(y<=100)&&(y<pic1.ys);y++) pic1.p[pic1.ys-1-y][x>>1].dd=0x00FF0000; 
for (x=(x0+x1)>>1,y=0;(y<=100)&&(y<pic1.ys);y++) pic1.p[pic1.ys-1-y][x>>1].dd=0x0000FF00; 
+0

这将与matlab一起工作吗?我在matlab上工作的不是C –

+0

@AhmedHassaan是的算法是一样的,但是你需要端口代码和/或与matlab中的函数名称交换函数名称。一些步骤最有可能直接实现,如图像与双峰直方图二值化,floodfilling等 – Spektre

1

您可能会感兴趣在我们为开源软件开发的最新插件中,Icy http://icy.bioimageanalysis.org/

该插件名称为FitEllipsoid,它允许通过首先单击以快速将椭球体拟合到图像内容正交观点上的几点。 的指南,请访问:https://www.youtube.com/watch?v=MjotgTZi6RQ

另外请注意,我们提供在GitHub上Matlab和Java源代码(但我不能为他们提供,因为这是我在网站上首次亮相)。

+0

这里有两个额外的链接到Matlab和Java源代码: https://github.com/pierre-weiss/FitEllipsoid https:// github.com/pierre-weiss/FitEllipsoid_Icy –