How to transform a curve into a circle ?

I was looking at this video about 3D topology - https://www.youtube.com/watch?v=wO61D9x6lNY&t=673 … the part I’m interested in though is where they are transforming a curved shape into a circle (or at least, in this case, a cross-section of a sphere).

Now I know it’s possible to do this with paths in GIMP (and made easier with plugins developed by Ofnuts), but is it possible to do it from the command line with G’MIC? Perhaps using mathematical expressions along with some of the image drawing commands?

It’s probably much more difficult than even I realise, but thought I’d ask anyway!

This does look different than having one shape transition to another (already defined) target shape if that is what you mean, i.e.:

  1. Here is an octagon and a circle.
  2. Now create intermediaries.

Or maybe not?

What is the constraint? That the circle’s circumference must match the perimeter/path of the original shape? And that the transitional images have to maintain that length? Without the shape breaking apart?

If there is an existing algorithm, for sure it could be translated into G’MIC, where anything is possible. :tm: :stuck_out_tongue:

1 Like

I’m guessing it would have to be the same length, okay maybe transforming it into any shape is going a bit far - but the video at the timestamp posted basically sums up the gist of the problem I would like to solve.

I heard something about the Whitney-Graustein theorem, but no idea if that really has relevance for this particular transformation. It’s the plotting of the intermediate paths that I guess is the nub of the problem.

This idea reminds me the Mean Curvature Flow, which can be approximated by successions of gaussian blurs + thresholding, like in the example below:

foo :
  shape_cupid 512
  repeat inf {
    b 10 ge 50%
    w.
    wait 20
  }

anim_to_circle

It does not preserve the shape area though, but this can be probably done with a bit more complex algorithm.

2 Likes

Quick attempt with a hack:

foo :
  shape_cupid 512
  +distance. 1 ic:=ic rm.

  repeat inf {
    b 10 ge 50%
    distance. 1
    lt {ic-$ic}
    w.
    wait 20
  }

anim_to_circle

2 Likes

Third iteration : This one better preserves the initial shape’s area :

foo :
  shape_star 256 # must be a binary image with values in { 0,1 }
  area:=is # Value of the area to preserve
  e "- Initial Area : "$area\n

  repeat inf {
    b {1*1.01^$>} ge 50%
    distance. 1
    eps=0 repeat inf { +le. $> narea:=is rm. if $narea>$area eps:=max(0,$>-1) break fi }
    le. $eps
    e "\r- Area = "{is}"    "
    w.
    wait 20
  }

anim_to_star

2 Likes

Hey, this is fun :wink:
Fourth iteration : use histogram of distance function to quickly get an idea on how areas evolve according to potential distance thresholding.

foo :
  shape_dragon 480 >. 0 # must be a binary image with values in { 0,1 }
  area:=is # Value of the area to preserve
  repeat inf {
    b. {1.5*1.01^$>},0 ge. 50%
    distance. 1
    +histogram. 100,0,9.99 cumulate. x >. $area eps:=max(xM-1,0)/10 rm.
    le. $eps
    
    # Generate view.
    +g. xy,0 a[-2,-1] c norm. >. 0
    100%,100%,1,3,"i(#-1)?[ 255,255,255 ]:i(#-2)?[ 128,32,0 ]:[ 0,0,0 ]"
    w.
    rm[-2,-1]

    wait 20
  }

anim_dragon

anim_dragonfly

4 Likes

Hey! That’s incredibly cool. Thanks for taking the time to cogitate on this scenario, David!
I could never work out stuff like that in a million years!

1 Like

When all it takes is a few lines of G’MIC code, it’s more of a hobby than a job :slight_smile:

Believe it or not, but for fun, I asked Google Gemini to explain me this command.
And it did work… I suppose that in a few years, I won’t be of any use helping people on the forum anymore :slight_smile:

1 Like

Well, I find that hard to believe! After all you’re a lot more able to come up with various workable solutions to a random problem somebody dropped into the forum, whilst injecting humour into the discussion, I don’t think the LLMs have quite got the hang of all that yet… (no matter how hard they try)!

Back to the scenario though, is there a way of making a command one-liner to do some of the above, to maybe output a series of images for example? I cannot ‘display’ or ‘generate’ anything using my Android tablet (no X-server type setup).

1 Like

Still the question remains, anyone, how can a line figure shape transform into a circle, somehow like the following example…

Any examples should be command-line friendly (as per gmic cli) and have an option to output the results as image files over any (reasonable) amount of iterations.

Here’s a cleaner version using crop and gui_rep_majority_threshold - turned out well I think (I also included slightly more of the clip for completion purposes and muted the sound)

I have addressed a similar problem, using ImageMagick.

Suppose we have two fully-connected shapes, where one shape is entirely within the other shape. (If they are not “entirely within”, then scale-rotate-translate one until they are “entirely within”, then do a proportion of the opposite transformation to the result.)

For example, we have a white “lake” surrounded by black “land”. The lake contains a black island:

We want to interpolate (morph) one shape into the other. This means we want a linear transition from one shape to the other, and we need to know the shape at any interpolation.

Here is a solution that is black at the island and white at the mainland, with shades of gray at interpolations:

I made that image by doing a “-morphology Distance” for each of the inputs, and combining those with what I call a “fan composition”.

We can express this another way, showing ten interpolations:

Details at Islands.

EDIT: Of course, we can animate:

x

It’s not the subject here but i like how it looks a bit like ink spreading on paper.
Would be cool to explore this…

anim_dragon

foo :
  shape_dragon 420 r. 512,512,1,1,0,0,0.5,0.5 # Input binary shape

  # Extract contour parameterization of largest component.
  ge. 50% area0:=is # Initial area
  l. {
    edgels -1 k[{argmax(${"-lof h"})}] channels. 0,1 f. "I==J[-1]?[-1,-1]:I" discard. -1 r. 1,50%,1,2,-1
    1,32,1,1,y%2 r. 1,{-2,h} a[-2,-1] c
  } => pts
  x0,y0:=i0,i1
  512,512,1,3,"lerp([0,0,0],[0,32,128],y/h)" => background
  (0^0^0^0,255^255^255^255,255^0^0^255) => colormap

  # Perform curve evolution.
  repeat inf {

    # Render silhouette on canvas.
    {background,[w,h]} eval[pts] "i(#-1,i0,i1) = 1" flood. 0,0,0,0,0,1,-1 >=. 0
    rm[pts]

    # Evolve silhouette and reparameterize.
    b. {min(20,1.15^$>)},0 >=. 0.5 distance. 1
    +histogram. 100,0,99 cumulate. >. $area0 val:=max((xM-1)/10,0) rm.
    le. $val
    l. {
      edgels -1 k[{argmax(${"-lof h"})}] channels 0,1 f. "I==J[-1]?[-1,-1]:I" discard. -1 r. 1,50%,1,2,-1
      1,100%,1,2,[$x0,$y0] -. .. norm. ind:=ym rm.
      shift. 0,{-$ind},0,0,2 x0,y0:=i0,i1
      1,32,1,1,y%2 r. 1,{-2,h} a[-2,-1] c
    } => pts

    # Render curve.
    {background,[w,h]} eval[pts] "I(#-1,i0,i1) = i2?2:1"
    dilate_circ. 3 map. [colormap] +ja[background] . rm..
    w.

    on. frame.jpg,$>
    rm.
  }
3 Likes

Is it just me or they look almost equally spaced? I did wanted to somehow generate equal spaced points along bezier curves.

Wow! Thanks for that David, absolutely astounded by that incredible code! And I’ll certainly be studying it closely.
I ran it on my system and it works just fine, thanks again for the time and effort you put into it! (Probably 5 minutes knowing you, lol… :wink:)

1 Like

@snibgo Fascinating stuff! ImageMagick is a great tool.

Much of it has to do with @snibgo’s math, scripting and tolerance for cmd.exe.

1 Like

@David_Tschumperle Nice! Will that method work on @Mushy’s example where the curve overlaps with itself?