14

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

Yaroslav Bulatov
  • 7,793
  • 1
  • 19
  • 44
  • You probably want to play with the frame durations in GIF. This post may be relevant. – yarchik Sep 12 '21 at 13:23
  • 1
    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. – Greg Hurst Sep 12 '21 at 21:09

1 Answers1

6

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 = (spreadsubsample - 1) + 2; middleFrame = (width - 2)/2; expr = makePlot[middleFrame, width]; expr /. x -> # & /@ Range[0, spreadsubsample - 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]]

Yaroslav Bulatov
  • 7,793
  • 1
  • 19
  • 44
  • 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). – Greg Hurst Sep 14 '21 at 20:11
  • 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 – Yaroslav Bulatov Sep 14 '21 at 20:16
  • 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, https://www.wolframcloud.com/obj/yaroslavvb/newton/forum-blending-rotation.nb – Yaroslav Bulatov Sep 14 '21 at 20:53
  • 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. – Greg Hurst Sep 14 '21 at 20:59
  • Thanks, thought about using Rasterize too, currently figuring out how to get rid of the quality drop I see with Rasterize -- https://mathematica.stackexchange.com/questions/255544/rasterize-drops-anti-aliasing-changes-axes-thickness – Yaroslav Bulatov Sep 14 '21 at 21:09