The well-known Chan-Vese segmentation algorithm from the paper “Active Contours Without Edges,” is a great example of active contours. This technique deforms an initial curve so that it separates foreground from background based on the means of the two regions. The technique is very robust to initialization and gives very nice results when there is a difference between the foreground and background means.

In this video, the curve begins as a square. As time goes on the square changes shape so that it does a better and better job of separating the image into a light area and a dark area.

Below is a download-able Matlab demo. The code is very easy to read, and could be the foundation for lots of other active contour segmentation techniques.

I recently added some new active contour stuff based on a more complex (and sometimes more capable energy). Check out the latest results, and the full project writeup which is a little older!

Published by Shawn Lankton

Most recently, Shawn led sales and marketing at Diligent Corporation where he participated in a $624M exit in 2016. In his role, he rapidly created a high-functioning and efficient revenue engine that quadrupled revenue growth while halving CAC. Before Diligent, he founded the "fast growth tech" practice at McKinsey & Co, through which he consulted with SaaS companies from $20 to $250M in ARR. At McKinsey, he was known for his expertise in enterprise sales, marketing, pricing, and growth strategy. Shawn earned his Ph.D. in electrical engineering and computer vision from Georgia Tech.
View all posts by Shawn Lankton

27 thoughts on “Active Contours”

Very good.Have you done in 3D?

Hello,
I am using your active contours without edges (regionbased_seg) code and i really like it – thank you for your contribution. I have a few questions/comments.
—–
1) your dphidt only normalizes the F associated with exterior/interior means (i.e. F_ext and F_int). However, shouldn’t the total force (which includes the curvature term) be normalized. For example, you have:
>>dphidt = F./max(abs(F)) + alpha*curvature;
but shouldn’t it be:
>>dphidt = F + alpha*curvature;
>>dphidt = dphidt./(max(abs(dphidt)));

2) when you find the dphidt:
>>dphidt = F./max(abs(F)) + alpha*curvature;
the F term is added. According to Chan/Vese’s Transaction on IP paper, eq.9, should the F1 term (interior force) be subtracted and the F2 term (ext force) be added? Therefore, shouldn’t the update equation be:
>>dphidt = (- F) + alpha*curvature;

I have checked this a few times. Please let me know what you think.
thanks,
April K

I have the same question with April K. Also, I wonder the curvature equation in your code that why mutiply *(phi_x2 + phi_y2).^(1/2) in the end of the equation.

I have tried to remove (phi_x2 + phi_y2).^(1/2)from the equation and it works well.

thanks,
bzf

er… it seems that my english is not adequated to express my question clearly.

Hello BZF,

I have noticed that some implementations include the (phi_x2 + phi_y2).^(1/2) term. after i made the modifications above (in my original comments #1) #2)), i tried removing the (phi_x2 + phi_y2).^(1/2) from the curvature term, and it didn’t make a big difference.

Do you agree with my original questions?

Hi!

I found the algorithm very useful. However, I have a question about its usability:
Is it possible that the algorithm is used in order to segment regions which are non foreground/background? What I mean is to set manually the initialization mask at a certain part and not let the algorithm set it automatically.

Thanks.

AprilK tracked me down on IM and got some answers out of me. Here’s the jist:

1) You caught me! That’s a hack : ) It makes the alpha parameter a bit more robust (espicially if you change the energy functional around).

2) I use F instead of (-F) because my SDF is upside down from the Chan-Vese implementation.

BZF: The phi_x^2 and phi_y^2 are part of the definition of Gaussian Curvature. They come out when you do the Calculus of Variations to minimize the arclength of a curve.

Alex: The demo sets the initialization as a box, but for most applications you will want to set the initial shape to something that is relevant.

Just pass the function a matrix of 1’s and 0’s where 1 is a guess at the foreground and 0 is a guess at the background.

3D codes are coming soon!

Dear shawn,

I have visit your homepage, and get localized active contours code in your paper “Localizing region-based active contours”. I think it is a good idea to deal with heterogeneous image.

I write this letter to you because I have some question to ask you about your code in the integral of local force in Eq.(14) in your paper “Localizing region-based active contours”.

I really cannot understand the integral about the y in your matlab code -(u-v).*(2.*I(idx)-u-v) for chan-vese model.

I look forward your relpy.

Thanks in advance!

richer: In the Matlab codes, the ‘x’ and ‘y’ variables from the paper aren’t easy to see because I use colon (:) notation in the Matlab instead of indexes. It would be more apparent in C++ code.

Dear Shawn,
Thanks for your contribution, it works really well.

I have a question for you:

In the paper of chan-vese (‘Active contours without edges’) they use the following curvature term:

div( gradient(phi) / |gradient(phi)| )

but I don’t understand why you have used other expression.

Thanks

jf.garamendi, when you expand “div( gradient(phi) / |gradient(phi)| )” you will get exactly what is the last line of get_curvature function.

I have tried the algorithm,and it doesn’t work well in the hold case, how could i solve this problem with your codes? thanks

ps: hole case means the object have a hold within its boundary

Hi. Am trying to convert your code to java. Was wondering if you could explain what the bwdist function does. Does it compute the distance from each pixel to he next or does it find the nearest neighbouring pixel and then find the distance between them

Hi,I’ve read your papers“Active Contours Without Edges,”and have some confused.

In the paper of MS Energy part Eq(17)
It looks as:
[(I-u)^2 / Au] – [(I-v)^2 / Av]

And in your matlab code it shows
F = -((u-v).*((I(idx)-u)./Ain+(I(idx)-v)./Aout))
just like
[(I-u)*(v-u) / Au] – [(I-v)*(u-v)] / Av]

I really cannot understand if that’s right or not.
I look forward your relpy.
Thanks in advance!

I got it, sorry!!

I am using your Active Contours Without Edges program.

Just wanted to so it had helped alot with my thesis and is a brilliant piece of work!

Many thanks for publishing it.

Dear Sir,
I am persuing Ph D from India. I am doing the work in inthe same field.Will you please give me the matlab code for localizing region based active contour using local histogram separation energy measurement.

Thanking You,

Hiren Mewada

All of the codes will be made available after my defense (September ’09). In the meanwhile, please see this post on sparse field active contours for a much faster active contour implementation.

i like to work on active contours for biomedical images. will you suggest where to and with what to start. i mean fundamentals.

with regards,

shanka

to add to the above, i feel very difficult to get into theory, pls help me.

shanka

Dear Shawn,

thanks a lot for this toolbox. I have a few questions about the finite differences used to compute the curvature, function curvature = get_curvature(phi,idx) (these questions are certainly very basic !):
1. for the first order central differences, phi_x and phi_y, why don’t you divide by 2 ?
2. Where does the phi_xy expression come from ? I suppose you derive phi_x against y, using again the central difference ? If so, are you sure about the sign of phi_xy (I would take the opposite) and is the coefficient 0.25 consistent with the absence of the coefficient 0.5 in the expressions of phi_x and phi_y ? If not, could you explain me how you get this expression?
3. I am not convinced either about the multiplying term “(phi_x2 + phi_y2).^(1/2)” in the calculus of the curvature (div(grad(phi)/|phi|)). Could you give us any reference ?

Very good.Have you done in 3D?

Hello,

I am using your active contours without edges (regionbased_seg) code and i really like it – thank you for your contribution. I have a few questions/comments.

—–

1) your dphidt only normalizes the F associated with exterior/interior means (i.e. F_ext and F_int). However, shouldn’t the total force (which includes the curvature term) be normalized. For example, you have:

>>dphidt = F./max(abs(F)) + alpha*curvature;

but shouldn’t it be:

>>dphidt = F + alpha*curvature;

>>dphidt = dphidt./(max(abs(dphidt)));

2) when you find the dphidt:

>>dphidt = F./max(abs(F)) + alpha*curvature;

the F term is added. According to Chan/Vese’s Transaction on IP paper, eq.9, should the F1 term (interior force) be subtracted and the F2 term (ext force) be added? Therefore, shouldn’t the update equation be:

>>dphidt = (- F) + alpha*curvature;

I have checked this a few times. Please let me know what you think.

thanks,

April K

I have the same question with April K. Also, I wonder the curvature equation in your code that why mutiply *(phi_x2 + phi_y2).^(1/2) in the end of the equation.

I have tried to remove (phi_x2 + phi_y2).^(1/2)from the equation and it works well.

thanks,

bzf

er… it seems that my english is not adequated to express my question clearly.

Hello BZF,

I have noticed that some implementations include the (phi_x2 + phi_y2).^(1/2) term. after i made the modifications above (in my original comments #1) #2)), i tried removing the (phi_x2 + phi_y2).^(1/2) from the curvature term, and it didn’t make a big difference.

Do you agree with my original questions?

Hi!

I found the algorithm very useful. However, I have a question about its usability:

Is it possible that the algorithm is used in order to segment regions which are non foreground/background? What I mean is to set manually the initialization mask at a certain part and not let the algorithm set it automatically.

Thanks.

AprilK tracked me down on IM and got some answers out of me. Here’s the jist:

1) You caught me! That’s a hack : ) It makes the alpha parameter a bit more robust (espicially if you change the energy functional around).

2) I use F instead of (-F) because my SDF is upside down from the Chan-Vese implementation.

BZF: The phi_x^2 and phi_y^2 are part of the definition of Gaussian Curvature. They come out when you do the Calculus of Variations to minimize the arclength of a curve.

Alex: The demo sets the initialization as a box, but for most applications you will want to set the initial shape to something that is relevant.

Just pass the function a matrix of 1’s and 0’s where 1 is a guess at the foreground and 0 is a guess at the background.

3D codes are coming soon!

Dear shawn,

I have visit your homepage, and get localized active contours code in your paper “Localizing region-based active contours”. I think it is a good idea to deal with heterogeneous image.

I write this letter to you because I have some question to ask you about your code in the integral of local force in Eq.(14) in your paper “Localizing region-based active contours”.

I really cannot understand the integral about the y in your matlab code -(u-v).*(2.*I(idx)-u-v) for chan-vese model.

I look forward your relpy.

Thanks in advance!

richer: In the Matlab codes, the ‘x’ and ‘y’ variables from the paper aren’t easy to see because I use colon (:) notation in the Matlab instead of indexes. It would be more apparent in C++ code.

Dear Shawn,

Thanks for your contribution, it works really well.

I have a question for you:

In the paper of chan-vese (‘Active contours without edges’) they use the following curvature term:

div( gradient(phi) / |gradient(phi)| )

but I don’t understand why you have used other expression.

Thanks

jf.garamendi, when you expand “div( gradient(phi) / |gradient(phi)| )” you will get exactly what is the last line of get_curvature function.

I have tried the algorithm,and it doesn’t work well in the hold case, how could i solve this problem with your codes? thanks

ps: hole case means the object have a hold within its boundary

Hi. Am trying to convert your code to java. Was wondering if you could explain what the bwdist function does. Does it compute the distance from each pixel to he next or does it find the nearest neighbouring pixel and then find the distance between them

Hi,I’ve read your papers“Active Contours Without Edges,”and have some confused.

In the paper of MS Energy part Eq(17)

It looks as:

[(I-u)^2 / Au] – [(I-v)^2 / Av]

And in your matlab code it shows

F = -((u-v).*((I(idx)-u)./Ain+(I(idx)-v)./Aout))

just like

[(I-u)*(v-u) / Au] – [(I-v)*(u-v)] / Av]

I really cannot understand if that’s right or not.

I look forward your relpy.

Thanks in advance!

I got it, sorry!!

I am using your Active Contours Without Edges program.

Just wanted to so it had helped alot with my thesis and is a brilliant piece of work!

Many thanks for publishing it.

Dear Sir,

I am persuing Ph D from India. I am doing the work in inthe same field.Will you please give me the matlab code for localizing region based active contour using local histogram separation energy measurement.

Thanking You,

Hiren Mewada

All of the codes will be made available after my defense (September ’09). In the meanwhile, please see this post on sparse field active contours for a much faster active contour implementation.

i like to work on active contours for biomedical images. will you suggest where to and with what to start. i mean fundamentals.

with regards,

shanka

to add to the above, i feel very difficult to get into theory, pls help me.

shanka

Dear Shawn,

thanks a lot for this toolbox. I have a few questions about the finite differences used to compute the curvature, function curvature = get_curvature(phi,idx) (these questions are certainly very basic !):

1. for the first order central differences, phi_x and phi_y, why don’t you divide by 2 ?

2. Where does the phi_xy expression come from ? I suppose you derive phi_x against y, using again the central difference ? If so, are you sure about the sign of phi_xy (I would take the opposite) and is the coefficient 0.25 consistent with the absence of the coefficient 0.5 in the expressions of phi_x and phi_y ? If not, could you explain me how you get this expression?

3. I am not convinced either about the multiplying term “(phi_x2 + phi_y2).^(1/2)” in the calculus of the curvature (div(grad(phi)/|phi|)). Could you give us any reference ?

Many thanks for your help,

FT

@Flo Great questions, and some are bugs!

Please check the newest (fastest, most correct) implementation found here:

http://www.shawnlankton.com/2009/04/sfm-and-active-contours/