Antialiased Graphics

A rasterized image may contain bit-mapped representations of lines and smooth surfaces.  Because this is displayed as a bitmap, there is no notion of a line or surface as there is in a vectorized language.  Bitmaps represent the image as an array of discrete pixels.  This discrete nature can make lines and surface edges appear jagged.  To overcome this limitation, bit-mapped images can be antialiased if the bitmap was created from vectorized sources.  One method of antialiasing involves enlarging the original vectorized source graphic by some integer factor and then rasterizing the enlarged image.  A set of pixels in the enlarged image are replaced by the average value.  If the enlarged raster was enlarged 4 times in each direction, then the pixels are averaged over every sixteen pixels.  The resulting image is 1/4 the enlarged size which is the size of the original image before enlargement.  However, because of the averaging process, the elements now look smoother.  This process is antialiasing.

The following code creates an antialised version of a given graphic.

Anti-Aliasing Code

The following code uses a 4x4 smoothing kernel and ListConvolve.  Essentially, the graphic object is exported 4x its normal size and is then re-imported.  ListConvolve is then used on this larger bitmap to obtain a smoothed version of the original graphic in rasterized format.

\(aa[gr_] := 
      Module[{siz, kersiz, ker, dat, as, ave, is, 
          ar}, 
is = 
          ImageSize /. Options[gr, ImageSize]; 
ar = 
          AspectRatio /. 
            Options[gr, AspectRatio]; 
If[\(! NumberQ[
              is]\), is = 288]; 
kersiz = 
          4; 
img = 
          ImportString[
            ExportString[gr, "PNG", 
              ImageSize -> \((is\ kersiz)\)], "PNG"]; 
siz = 
          Reverse@\(Dimensions[
                img[\([1, 1]\)]]\)[\([{1, 2}]\)]; 
ker = 
          Table[N[1/kersiz\^2], {kersiz}, {kersiz}]; 
dat = 
          N[img[\([1, 1]\)]]; 
as = 
          Dimensions[dat]; 
ave = 
          Partition[
            Transpose[\(Flatten[
                    ListConvolve[ker, dat[\([All, All, #]\)]]] &\) /@ 
                Range[as[\([3]\)]]], 
            as[\([2]\)] - kersiz + 1]; 
ave = 
          Take[ave, 
            Sequence @@ \((\({1, \(Dimensions[ave]\)[\([#]\)], kersiz} &\) /@ 
                  Range[Length[Dimensions[ave]] - 
                      1])\)]; 
Show[
          Graphics[
            Raster[ave, {{0, 0}, siz/kersiz}, {0, 255}, 
              ColorFunction -> RGBColor]], 
          PlotRange -> {{0, siz[\([1]\)]/kersiz}, {0, 
                siz[\([2]\)]/kersiz}}, ImageSize -> is, 
          AspectRatio -> ar]
]\)

Applying the Algorithm

Here is a ParemetricPlot3D that results in a typical rendered graphic.  Close examination of the bounding box and the mesh on the surface reveals many jagged lines.  We will attempt to smooth these features using this antialiasing technique.

original = 
      ParametricPlot3D[{((2 (Cos[u] + u Sin[u]) Sin[v])/(1 + u u Sin[v] Sin[v])), ((2 (Sin[u] - u Cos[u]) Sin[v])/(1 + u u Sin[v] Sin[v])), 
          Log[Tan[(v/2)]] + ((2 Cos[v])/(1 + u u Sin[v] Sin[v]))}, {u, -4, 4}, {v, 0.05, π}, PlotPoints -> 50, 
        ImageSize -> 600]

Since some aspects of the above graphic do not scale with image size, we must take extra steps to make sure they are handled correctly.  The features requiring extra care are the narrow features such as lines and fonts.  Without special care, narrow black lines will appear grey in the resulting image since they are averaged against the white background.  To overcome this, we will increase the line widths by 4 times and also enlarge font sizes by 4 times.

gr = 
        ParametricPlot3D[{((2 (Cos[u] + u Sin[u]) Sin[v])/(1 + u u Sin[v] Sin[v])), ((2 (Sin[u] - u Cos[u]) Sin[v])/(1 + u u Sin[v] Sin[v])), 
            Log[Tan[(v/2)]] + ((2 Cos[v])/(1 + u u Sin[v] Sin[v])), {EdgeForm[AbsoluteThickness[4]]}}, {u, -4, 4}, {v, 0.05, π}, 
          PlotPoints -> 50, ImageSize -> 600, 
          TextStyle -> {FontSize -> 40}, 
          BoxStyle -> {AbsoluteThickness[4]}, 
          DisplayFunction -> Identity];

Now, we are ready to antialias the graphic.  Be patient as this might take a couple of minutes.  The larger the original image size, the longer the algorithm will take.

finalgraphic = aa[gr]

The resulting image is much improved although does have some side-effects and shows some of the difficulties with implementing a robust antialiasing algorithm.  Notice that the tick labels are lighter in color than the original.  Fonts can be especially tricky to antialias correctly since the glyphs often have narrow strokes and therefore take on a lightened appearance when averaged against a lighter background color.  However, the positive results often exceed the negatives.