Animation: Skeleton to silhouette

Just for fun, this is a simple G’MIC script I wrote to render an animation showing how a 2D silhouette can be reconstructed from its skeleton and the values of the distance function at each skeleton point.

animate_reconstruction :
  shape_cupid 512

  # Extract set of skeleton vertices.
  +distance. 0 +skeleton.. 0 1,1,1,3 eval.. "i?da_push([x,y,i(#-3)]); end(resize(#-1,1,da_size(),1,3,0))" rm[-3,-2]

  # Render shape outline + skeleton.
  [0],[0] eval[1] "ellipse(#-1,i0,i1,1,1,0,1,i2)" +n. 0,255 map. hot neq.. 0 rv[-2,-1] dilate. 3 n. 0,255 a[-2,-1] c nm. skeleton

  # Render animation.
  100%,100%,1,3,255 nm. canvas
  repeat 3*h#1
    x,y,r={1,I[int(u(h)%h)]}
    circle[canvas] $x,$y,$r,0.25,0xFFFFFFFF,0
    +blend. [skeleton],alpha,1 w. rm.
  done

Here is the result:

ezgif.com-gif-maker (2)

5 Likes

A small improvement :

bug :
  shape_rooster 800

  # Extract set of skeleton vertices.
  +distance. 0 +skeleton.. 0 1,1,1,3 eval.. "i?da_push([x,y,i(#-3)]); end(resize(#-1,1,da_size(),1,3,0))" rm[-3,-2]
  i.. 1,100%,1,1,u a[-2,-1] x sort. +,y z. 1,1

  # Render shape outline + skeleton.
  [0],[0] eval[1] "ellipse(#-1,i0,i1,1,1,0,1,i2)" +n. 0,255 map. hot neq.. 0 rv[-2,-1] dilate. 3 n. 0,255 a[-2,-1] c nm. skeleton

  # Render disk drawing animation.
  100%,100%,1,3,255 nm. canvas
  f=0 s=1
  repeat h#1
    x,y,r={1,I[$>]}
    circle[canvas] $x,$y,$r,0.25,0
    circle[canvas] $x,$y,$r,0.5,0xFFFFFFFF,0

    if !($>%int($s))
      +blend[canvas] [skeleton],alpha,1 r2dx. 400 w.
      on. frame.png,$f f+=1 rm.
    fi
    s:=min(100,$s*1.02)
  done

  # Fade skeleton out.
  repeat 50
    +blend[canvas] [skeleton],alpha,{lerp(1,0,min(1,$>/10))} r2dx. 400 w.
    on. frame.png,$f f+=1 rm.
  done

renders this:

anim_rooster

1 Like

Now do a human skeleton. See what happens. :stuck_out_tongue:

Like this?

anim_human

1 Like

Ha ha, yes! I wanted to see a funny human skeleton. I have a sharp bum too.

I had no idea it could encode that, something like a projection/hologram. No doubt it has many other uses I don’t about as well!

Edit: thinking about it, it wouldn’t be too difficult to bend the skeleton and produce new painful looking arrangements!

Another idea would be to have a way to distort the skeleton, then reconstruct the silhouette, maybe for doing animation of characters or things like that.

I forgot to mention that the idea of animation stimulated another thought: what if you combine that with Algorithm for shape registration : Iterative Closest Point?

Say, we have a stick figure trying to run away from a swarm of bees. :running_man: :honeybee: :stuck_out_tongue:

I wonder if this could be used for simplifying shapes by pruning the skeleton. Or, if done in reverse somehow, simplifying the skeleton.

And maybe this could be used in vectorising bitmaps.

Edit: Also, what about resizing shapes by resizing the skeleton.

I’ve tried silhouette upscaling, by :

  1. Extracting the points of the skeleton and their corresponding radius.
  2. Multiply everything by 2.
  3. Reconstruct the silhouette from this new set of points.

That works, but that is not outstansing IMHO. You still get staircasing effects. I’m not sure it works better than resizing the whole silhouette image then threshold it to get rid of the aliasing pixels.

foo :
  shape_cupid 200

  # 2x upscale shape using upscaled skeleton.
  +distance. 0 +skeleton.. 0
  1,1,1,3 eval.. "i?da_push([x,y,i(#-3,x,y)]); end(resize(#-1,1,da_size(),1,3,0))" rm[-3,-2]
  {0,2*[w,h]} eval.. ">ellipse(#-1,2*i0,2*i1,2*i2,2*i2,0,1,1)"
  rm..

On the contrary, I got interested in “edgels” again :
An edgel is one edge of a pixel (so each pixel has 4 edgels), and it can be used to analyze edges composed by several pixels. In my case, I’ve used them to extract a parametric model of silhouettes. Once you get this parametric representation, it becomes quite simple to do, e.g. curve simplification.
Yesterday, I’ve added a new command edgels in the stdlib, and with it you can do things like this:

foo2 :
  shape_dragon 300 area_fg. 0 ge. 100%
  +edgels
  l. s c avg0,std0={0,[ia,sqrt(iv)]} avg1,std1={1,[ia,sqrt(iv)]} a c endl

  N=300
  repeat $N
    +b[1] y,{lerp(0,900,($>/$N)^3)},2
    l. s c
      -[0] {0,ia} *[0] {0,$std0/sqrt(iv)} +[0] $avg0 navg0={0,ia}
      -[1] {1,ia} *[1] {1,$std1/sqrt(iv)} +[1] $avg1 navg1={1,ia}
    a c endl

    +f[0] 0 eval.. "const boundary = 2; polygon(#-1,2,i0,i1,i(0,y + 1,0,0),i(0,y + 1,0,1),1,2)"
    flood. $navg0,$navg1,0,0,0,1,1

    n. 0,255
    w. rm[-2,-1] wait 20
  done

curve_motion

1 Like