區域生長算法

算法介紹
區域生長,指將前景種子點擴展爲更大區域的過程。

預備知識
編程工具:VS2010, CMAKE
編程語言:C++
編程庫:ITK
數據結構:棧,通過棧實現區域生長算法
數據:三維的病變視網膜圖像
目的:分割視網膜圖像中的病變區域,即色素上皮層脫離區域。

實現過程
(1) 讀入原圖像,前景種子點圖像;
說明:前景種子點由形態學方法自動獲取,也可以手動標註前景種子點。
(2) 定義區域生長的準則;
(3) 前景種子點入棧;對前景種子點逐個遍歷,在當前種子點的26鄰域內,如果有像素點滿足生長條件,則該像素點入棧;直到棧爲空,則算法結束;
(4) 添加約束條件,對區域生長結果進行限制;
(5) 將區域生長結果疊加在原圖上,查看具體效果。

具體代碼如下,並在VS2010上測試通過:

#include"itkImage.h"
#include"itkImageFileReader.h"
#include"itkImageFileWriter.h"
#include<iostream>
#include<string>
#include <fstream>
#include <stack>
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::ImageFileReader<InputImageType>ReaderType;
    typedef itk::ImageFileReader<SeedImageType>SeedReaderType;
    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;  
    }; 
    stack<seedpoint>seedS; 
    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::ImageFileWriter<OutputImageType>WriterType;
                        WriterType::Pointer Writer = WriterType::New();
                        Writer->SetFileName(argv[3]);
                        Writer->SetInput(dstImage);
                        Writer->Update();
                        return 0;
}

效果圖如下,紅色區域爲區域生長結果。其中,半圓形區域爲目標區域,其他爲誤分割區域。對於誤分割區域,可以用形態學處理算法進行後處理。
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章