区域生长算法

算法介绍
区域生长,指将前景种子点扩展为更大区域的过程。

预备知识
编程工具:VS2010, CMAKE
编程语言:C++
编程库:ITK
数据结构:栈,通过栈实现区域生长算法
数据:三维的病变视网膜图像
目的:分割视网膜图像中的病变区域,即色素上皮层脱离区域。

实现过程
(1) 读入原图像,前景种子点图像;
说明:前景种子点由形态学方法自动获取,也可以手动标注前景种子点。
(2) 定义区域生长的准则;
(3) 前景种子点入栈;对前景种子点逐个遍历,在当前种子点的26邻域内,如果有像素点满足生长条件,则该像素点入栈;直到栈为空,则算法结束;
(4) 添加约束条件,对区域生长结果进行限制;
(5) 将区域生长结果叠加在原图上,查看具体效果。

具体代码如下,并在VS2010上测试通过:

#include"itkImage.h"
#include"itkImageFileReader.h"
#include"itkImageFileWriter.h"
#include
#include
#include 
#include 
using namespace std;

int main(int argc, char* argv[])
{
    char* srcImagePath = argv[1];
    char* seedImagePath = argv[2];
    char* dstImagePath = argv[3];
    char* sur1Path = argv[4];//视网膜分层结果
    char* sur12Path = argv[5];//视网膜分层结果

    typedef itk::Image<unsigned short,3>InputImageType; 
    typedef itk::Image<unsigned short,3>OutputImageType;
    typedef itk::Image<unsigned char,3>SeedImageType;
    InputImageType::Pointer srcImage = InputImageType::New();
    OutputImageType::Pointer dstImage = OutputImageType::New();
    SeedImageType::Pointer seedImage = SeedImageType::New();

    typedef itk::ImageFileReaderReaderType;
    typedef itk::ImageFileReaderSeedReaderType;
    ReaderType::Pointer reader = ReaderType::New();
    SeedReaderType::Pointer readerSeed = SeedReaderType::New();
    reader->SetFileName(argv[1]);//原图像 
    readerSeed->SetFileName(argv[2]);//前景种子点图
    reader->Update();
    srcImage = reader->GetOutput();
    readerSeed->Update();
    seedImage = readerSeed->GetOutput();

    //允许种子点图像与源图像尺寸不同
    InputImageType::SizeType imgSize = srcImage->GetLargestPossibleRegion().GetSize();
    SeedImageType::SizeType seedImgSize = seedImage->GetLargestPossibleRegion().GetSize();

    //结果图开辟内存
    OutputImageType::IndexType index;
    index[0]=0;
    index[1]=0;
    index[2]=0;
    OutputImageType::SizeType size;
    size[0]=imgSize[0];
    size[1]=imgSize[1];
    size[2]=imgSize[2]; 
    OutputImageType::RegionType region;
    region.SetIndex(index);
    region.SetSize(size);
    dstImage->SetRegions(region);
    dstImage->Allocate();

    struct seedpoint
    {
        int x;
        int y;
        int z;  
    }; 
    stackseedS; 
    seedpoint point; 

    //读入原数据
    //unsigned short *srcData = new unsigned short[imgSize[0]*imgSize[1]*imgSize[2]];
    //通过GetBufferPointer()获取数据后,如果使用delete[] srcData 会导致程序崩溃
    //所以不使用new开辟内存,借助与ITK内存管理机制来实现
    const unsigned short* srcData  = srcImage->GetBufferPointer();//ITK数据转换为C++数据

    //memset初始化结果图指针,第三个参数为内存单元大小(字节数)
    unsigned short* resultData = new unsigned short[imgSize[0]*imgSize[1]*imgSize[2]];
    memset(resultData, 0, sizeof(unsigned short) * imgSize[0] * imgSize[1] * imgSize[2]);  

    //前景种子点入栈
    const unsigned char* seedData = seedImage->GetBufferPointer();//ITK数据转换为C++数据
    for(int k=0; k < seedImgSize[2]; k++)
        for(int j = 0; j < seedImgSize[1]; j++)
            for(int i = 0; i < seedImgSize[0]; i++)
            {
                if(seedData[k*seedImgSize[0]*seedImgSize[1]+j*seedImgSize[0]+i] == 255)//255为前景灰度值
                { 
                    point.x = i;
                    point.y = j;
                    point.z = k;             
                    seedS.push(point);
                }           
            }

            //初始化,将原图中所有像素点都标记为没有被遍历过
            bool*flag = new bool[imgSize[0]*imgSize[1]*imgSize[2]];  
            memset(flag, false, sizeof(bool) * imgSize[0] * imgSize[1] * imgSize[2]);

            //区域生长实现
            unsigned short intensity = 13000;//前景平均灰度值        
            unsigned short threshold = 3000;//前景与背景灰度值差异         

            while(!seedS.empty())//栈为空则推出循环
            {
                seedpoint temppoint;     
                point=seedS.top();//取栈顶元素      
                seedS.pop();//元素弹栈
                flag[point.z*imgSize[0]*imgSize[1]+point.y*imgSize[0]+point.x] = true;//标记为已遍历
                resultData[point.z*imgSize[0]*imgSize[1]+point.y*imgSize[0]+point.x] = 255;//标记为亮区域

                //图像边界出的像素点不进行处理
                if((point.x >= 1) && (point.x <= (imgSize[0] - 2)) &&
                    (point.y >= 1) && (point.y <= (imgSize[1] - 2)) &&
                    (point.z >= 1) && (point.z <= (imgSize[2] - 2))) 
                {
                    int x = point.x;
                    int y = point.y;
                    int z = point.z;

                    for(int i = -1; i <= 1; i++)
                        for(int j = -1; j <= 1; j++)
                            for(int k = -1; k <= 1; k++)
                            {
                                if(flag[(z + i) * imgSize[0] * imgSize[1] + (y + j) * imgSize[0] + x + k] == false &&
                                    srcData[(z + i) * imgSize[0] * imgSize[1] + (y + j) * imgSize[0] + x + k] >= intensity - threshold && 
                                    srcData[(z + i) * imgSize[0] * imgSize[1] + (y + j) * imgSize[0] + x + k] <= intensity + threshold &&
                                    (abs(srcData[(z + i) * imgSize[0] * imgSize[1] + (y + j) * imgSize[0] + x + k] - 
                                    srcData[z * imgSize[0] * imgSize[1] + y * imgSize[0] + x]) < threshold))
                                {
                                    resultData[(z + i) * imgSize[0] * imgSize[1] + (y + j) * imgSize[0] + x + k] = 255;//亮区域
                                    temppoint.x = x + k;
                                    temppoint.y = y + j;
                                    temppoint.z = z + i;
                                    seedS.push(temppoint);//新种子点入栈
                                    flag[(z + i) * imgSize[0] * imgSize[1] + (y + j) * imgSize[0] + x + k] = true;//标记为已遍历
                                }
                            }
                }
            }
            delete[] flag;//用完后立即释放资源
            flag = NULL;//避免野指针

            //读入视网膜分层数据,对区域生长结果后处理
            ifstream fin1, fin12;
            int (*sur1)[512] = new int[64][512];
            int (*sur12)[512] = new int[64][512];
            fin1.open(sur1Path, ios::in);
            fin12.open(sur12Path, ios::in);
            for(int z = 0; z < 64; z++)
                for(int x = 0; x < 512; x++)
                {
                    fin1>>sur1[z][x];
                    fin12>>sur12[z][x];
                }
                fin1.close();
                fin12.close();

                //C++数据转ITK数据
                OutputImageType::IndexType newvoxelIndex;
                for(int z = 0; z < imgSize[2]; ++z)    
                    for(int y = 0; y < imgSize[1]; ++y)
                        for(int x = 0; x < imgSize[0]; ++x)   
                        {  
                            newvoxelIndex[0] = x;
                            newvoxelIndex[1] = y;
                            newvoxelIndex[2] = z;    
                            if(y < sur1[z][x] || y > sur12[z][x])//依据视网膜分层结果对区域生长结果进行约束
                                dstImage->SetPixel(newvoxelIndex, 0);
                            else
                                dstImage->SetPixel(newvoxelIndex, resultData[z*size[0]*size[1]+y*size[0]+x]);
                        }
                        delete[] sur1;
                        sur1 = NULL; 
                        delete[] sur12;
                        sur12 = NULL;
                        delete[] resultData;
                        resultData = NULL;

                        //输出结果图
                        typedef itk::ImageFileWriterWriterType;
                        WriterType::Pointer Writer = WriterType::New();
                        Writer->SetFileName(argv[3]);
                        Writer->SetInput(dstImage);
                        Writer->Update();
                        return 0;
}

效果图如下,红色区域为区域生长结果。其中,半圆形区域为目标区域,其他为误分割区域。对于误分割区域,可以用形态学处理算法进行后处理。
区域生长算法_第1张图片

你可能感兴趣的:(ITK)