14
$\begingroup$

I'm looking to improve the quality of exported animations (for which I'm limited to animated gifs for compatibility reasons), and the default export looks a bit unnatural/jittery. I'm wondering if there's a way to improve this. Potential ideas:

  1. Simple motion blur
  2. Temporal antialiasing
step = 10;
genPlot[i_] := 
  Graphics[{Circle[{0, 0}, 1], Rotate[Line[{{-1, 0}, {1, 0}}], i]}];
images = Table[genPlot[angle], {angle, 0, Pi - Pi/step, Pi/step}];
Export["circle.gif", images, "AnimationRepetitions" -> Infinity, 
 "DisplayDurations" -> 1/50]

enter image description here

Animation frames can be rendered at any time granularity, but gif frame delays are multiple of 10ms, so 25/50 FPS is possible, but 30/60 is not. Most displays nowadays seem to have refresh rate of 60 Hertz, so using 1/50 delay you would expect a "freeze-frame" every 5 frames. On other hand, this video claims to be 50fps and looks smooth on my 60 Hz screen, so perhaps frame rate mismatch is not a significant contributor to jitter

Edit

simple blending makes it a bit better, but the line looks gray instead of black and the sector boundary looks clearly visible. Also 50x oversampling would be pretty expensive to compute for more general animations (ie, like this one)

radius = 50;
images2 = 
  Table[genPlot[angle], {angle, 0, Pi - Pi/(step*radius), 
    Pi/(step*radius)}];
images3 = Blend /@ Partition[images2, radius];
Export["circle2.gif", images3, "AnimationRepetitions" -> Infinity, 
 "DisplayDurations" -> 1/50]

enter image description here

$\endgroup$
2
  • $\begingroup$ You probably want to play with the frame durations in GIF. This post may be relevant. $\endgroup$
    – yarchik
    Commented Sep 12, 2021 at 13:23
  • 1
    $\begingroup$ Perhaps you could play with different weights in the second argument of Blend to give more of a trailing effect. This might help darken up the line. Gamma correction via ImageAdjust[im, {0, 0, γ}] might help with that too. $\endgroup$
    – Greg Hurst
    Commented Sep 12, 2021 at 21:09

1 Answer 1

7
$\begingroup$

I ended up using Blend combined with a helper function below that creates weights from Sin curve to average several frames together. enter image description here

IE, weights to smear each image over 5 frames, where each frame is a subsampled 3 times, the weights from genWeights[5, 3] look like this

enter image description here

(* generate sin plot centered at offset, 
 with given width . offset - width/2 .. offset + width/2 *)

makePlot[offset_, width_] := (
   (Cos[(2 \[Pi] (-offset + x))/width] + 1)/2
   );

(* generates weights for line spanning "spread" frames, each frame \
using "subsample" frames *)
genWeights[spread_, subsample_] := (
   Assert[OddQ[spread]];
   Assert[OddQ[subsample]];
   
   width = (spread*subsample - 1) + 2;
   middleFrame = (width - 2)/2;
   expr = makePlot[middleFrame, width];
   expr /. x -> # & /@ Range[0, spread*subsample - 1] // N
   );


spread = 5;
subsample = 3;
vals = genWeights[spread, subsample];
xvals = Transpose[{Range[Length[vals]], vals}]
ListPlot[Partition[xvals, subsample], InterpolationOrder -> 0, 
 Filling -> Axis, FillingStyle -> Thickness[.05], 
 PlotStyle -> PointSize[Large], 
 PlotLabel -> 
  StringForm["spread=``, subsample=``", spread, subsample]]
$\endgroup$
5
  • $\begingroup$ Very nice. How are the weights fed into Blend? If I understand correctly you feed in the entire half period of Sin as your weights? Would using the quarter period (from 0 to 1) make more sense? I wonder if it would give the line a black appearance on one side and a blur on the other (a trailing effect). $\endgroup$
    – Greg Hurst
    Commented Sep 14, 2021 at 20:11
  • $\begingroup$ It's done with Blend[images[[#]], weights] & /@ Partition[Range[totalFrames], 15, 3]. I'll experiment some more with half blending some on realistic images, currently my rotation animations like the ones here are looking jerky $\endgroup$ Commented Sep 14, 2021 at 20:16
  • $\begingroup$ Wow, Blend is slow on Graphics3D, much slower than Export or Rasterize, I ran out of patience waiting for 10 frames to Blend together, may need to do a combo Mathematica+Python solution for this, wolframcloud.com/obj/yaroslavvb/newton/… $\endgroup$ Commented Sep 14, 2021 at 20:53
  • $\begingroup$ FWIW Blend[Rasterize /@ frames[[#]], weights] is 6 times faster than Blend[frames[[#]], weights] on my machine. A bit more bearable. You could also look into using ParallelMap -- when doing so you'll need to wrap your call to Blend in UsingFrontEnd to invoke a sub front end. $\endgroup$
    – Greg Hurst
    Commented Sep 14, 2021 at 20:59
  • $\begingroup$ Thanks, thought about using Rasterize too, currently figuring out how to get rid of the quality drop I see with Rasterize -- mathematica.stackexchange.com/questions/255544/… $\endgroup$ Commented Sep 14, 2021 at 21:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.