Thursday, May 18, 2006

More on Region growing for vessel segmentation

The problem of using a trivial region growing algorithm which uses a one or two-fold threshold as its growing-criteria is that it is bound to "explode" in the case of heart-vessels. What usually follows this explosion is a "stack-overflow" exception which results from the large amount of stack space taken up by the recursive region growing process. I have been experimenting with a recursive region-growing algorithm, and it runs out of stack when run on the visual .net C++ compiler despite increasing the stack size to several hundred megabytes (using the /STACK option in the linker, or the /F option of the compiler). What actually happens is that the region explodes into the atrium of the heart where it ultimately runs into a stack exception. However, the algorithm segments correctly without a stack-exception if the lower and upper-thresholds are set to point to high-intensities (scalar value of 80-150).

Grey-value intensities in contrast enhanced MR angiography is directly proportional to the amount of blood flow in a region. The atrium of the heart presents high intensities, the reason of which is quite obvious. However, the venous drainages also present high voxel intensities in certain regions. I presume this is related with the high blood-pressure in these areas due to the narrowing of the vessels towards the drainages. Although we would expect the entire drainage to be of high-intensity, however, this is not the case. Close to the endings, we come across low intensities. So now this variation in intensity poses a challenging problem when using thresholds, i.e if we were to select a threshold for region-growing segmentation of the venous endings, we would have no choice but to pick a low-threshold value (in-order to capture the minute vein endings) and a high-threshold in order to capture the parts of the vein-endings where the blood flow is high. Since the Atrium of the heart has a high-intensity value, this causes the algorithm to explode into the atrium, causing a stack-exception at some point. The stack-exception can also be equally attributed to the lower-threshold value, since this threshold value causes the algorithm to further leave the atrium and flow into the numerous vessels which branch out of the atrium.

Following is an illustration of a veinous drainage as seen through an MRI contrast-enhanced angiography. This is only a slice through the entire volume captured in the MRI.

After meeting with Prof. Daniel today, he suggested to me to further investigate the possibility of increasing the stack space and to see if the recursive algorithm can be made to work. The best possible result is to get the entire MRI segmented by using low-high threshold values. I also suggested to him how people have been performing brain vessels segmentation using an atlas-based region growing approach as described in "Region growing segmenation of brain vessels: an atlas based approach" by N. Passat et. al. Brain-vessel segmentation seems to be different from heart-vessel segmentation; in essence brain vessels dont branch out of an "atrium" like structure.

Prof. Daniel suggested to me to use a non-recursive approach to region-growing whereby we could keep the growing-region to a spherical-like shape at any time instant. A recursive process tends to grow out the region in one direction at a time. Making the region grow out spherically could allow us to develop better cost functions which for example could utilise the compactness (surface area / volume) of the growing-region.

The 9-month transfer report is due soon.

2 comments:

Anonymous said...

If you are still interested in the recursive approach you can try and reduce stack usage usage without changing too much code around by trying to cut the number of local variables (and their size) you have in your function. If you need to have heavy duty data structures locally (like big arrays etc), keep a pointer to them and allocate them on the heap via new() when entering the function and then delete'ing it on exit:

foo()
{
char* p = new char[1000];
.
.
.
if (p) delete p;
}

Your milage may vary.

Anonymous said...

... or if you really want to be anal about it you can actually end up having only a local pointer on the stack for your function:

struct FOO_LOCALS
{
int* a;
char b[1000];
bool flag;
};

foo()
{
FOO_LOCALS* pLocals = new FOO_LOCALS;
.
.
.

if (pLocals) delete pLocals;
}