1. Purpose 2. Pixel methods 2.1. Static methods 2.1.1. AVERAGE 2.1.2. ACTIONAVG 2.1.3. MOSTUSED 2.1.4. LAST 2.1.5. FIRST 2.1.6. SOLID 2.1.7. FIRSTNMOST 2.1.8. LASTNMOST 2.1.9. LEASTUSED 2.2. Animated methods 2.2.1. CHANGELOG 2.2.1.1. Motion blur 2.2.2. LOOPINGLOG 2.2.3. LOOPINGAVG |
2.2.3.1. Motion blur 2.3. Summary 3. Masking methods 3.1. BLACK/BLANK/CENSOR 3.2. HOLE/ALPHA/TRANSPARENT 3.3. DELOGO/BLUR/INTERPOLATE 3.4. PATTERN/EXTRAPOLATE 4. Color quantization methods 4.1. Median-cut (aka. Heckbert) 4.2. Diversity 4.3. Blend-diversity 4.4. NeuQuant 5. Dithering 5.1. Gamma correction 5.2. Example 5.2.1. Dither error spread factor 5.2.2. Dither matrix size 5.2.3. Dither candidate count |
5.2.4. Dither contrast limiter 6. Color compare methods 6.1. RGB 6.2. CIE76 6.3. CIE94 6.4. CIEDE2000 6.5. CMC l:c 6.6. BFD l:c 6.7. Illuminants 7. Transformation 8. Caveats 8.1. Parallax motion 8.2. Flashes, fog and other transparent layers 9. Usage 10. Copying 11. Requirements 12. See also 13. Downloading |
It does this with a motion detection algorithm, a set of different pixel methods, and a simulated infinite 2D canvas — a 2D canvas that extends infinitely to all four directions (well, as infinite as 32-bit integers can get…)
![]() |
As a sample, here is the original animation (712100 bytes).
The animation was created literally by taking a screenshot
from the NES emulator every frame.
What follows below, is a list of the pixel methods supported
by animmerger,
The
|
You can see a faint trace of all animated actors that appeared in the animation. Mario moved very fast so his trace is quite difficult to spot.
Produced with commandline:
# animmerger -pa snaps/*.png -m0,8,256,16,020202,A64010,D09030,006E84,511800,FFFFFF
# mv tile-0000.png demo/method-a.png
An alternative implementation of "average" is also provided: "tinyaverage" (option -A). It requires less memory to store, but is less accurate to calculate.
If you want the color averages to be calculated through the YUV colorspace rather than the RGB colorspace, add the --yuv option (not supported by tinyaverage).
Note: If there is an actor that sits in a certain location
for a long time, it is also recorded.
In this example, there were none though.
This mode does not thus remove all actors, but it does remove
anything that wanders around.
Produced with commandline:
# animmerger -pm snaps/*.png -m0,8,256,16,020202,A64010,D09030,006E84,511800,FFFFFF
# mv tile-0000.png demo/method-m.png
Produced with commandline:
# animmerger -pl snaps/*.png -m0,8,256,16,020202,A64010,D09030,006E84,511800,FFFFFF
# mv tile-0000.png demo/method-l.png
The turtles are distorted, because they moved while the screen scrolled.
It is the same effect as if you move the paper in a desktop scanner during the scanning.
Produced with commandline:
# animmerger -pf snaps/*.png -m0,8,256,16,020202,A64010,D09030,006E84,511800,FFFFFF
# mv tile-0000.png demo/method-f.png
As seen here, it has shortcomings, too.
Produced with commandline:
# animmerger -pO snaps/*.png -m0,8,256,16,020202,A64010,D09030,006E84,511800,FFFFFF
# mv tile-0000.png demo/method-O.png
Most common of first 4:
Most common of first 10:
Most common of first 16:
First uncommon:
Least common of first 10:
Produced with commandline:
# for f in 4 10 -10 16 0; do
# animmerger -pF -f$f snaps/*.png -m0,8,256,16,020202,A64010,D09030,006E84,511800,FFFFFF
# mv tile-0000.png demo/method-Ff$f.png
# done
Most common of last 10:
Last uncommon:
Least common of last 10:
Produced with commandline:
# for f in 4 10 -10 16 0; do
# animmerger -pL -f$f snaps/*.png -m0,8,256,16,020202,A64010,D09030,006E84,511800,FFFFFF
# mv tile-0000.png demo/method-Lf$f.png
# done
Produced with commandline:
# animmerger -pe snaps/*.png -m0,8,256,16,020202,A64010,D09030,006E84,511800,FFFFFF
# mv tile-0000.png demo/method-e.png
You see some artifacts in the turtle and in Mario when they appear
near the top of the screen. This is because they were behind the
HUD (the text "WORLD 8-2" for instance), which was removed.
In the case of the turtle, the turtle's white pixels were also
removed, because the HUD removal was based on color as well
as coordinates.
Horizontal disappearance of the actors is because of the viewport
scrolling past them. They do not exist outside those parameters
in the original animation either.
Here is how the animation looks like, if the HUD is not removed. (246643 bytes)
Exteriors, i.e. content outside the "current" viewport of the animation
are colored as in the MostUsed pixel method.
This is evident in the trails left by the HUD as it scrolls by at different speeds.
Produced with commandline:
# rm tile-*.png tile-*.gif
# animmerger --gif -pc snaps/*.png -m0,8,256,16,020202,A64010,D09030,006E84,511800,FFFFFF
# gifsicle -O2 -o demo/method-c.gif -l0 -d3 tile-*.gif
The version with HUD intact was created with the same commandline,
except with the -m option removed.
The background for ChangeLog is normally generated with the MostUsed method, but it can be
explicitly controlled with the --bgmethod0 and --bgmethod1 options.
Here is how the above animation (HUD-less) looks like with --bgmethod0 first --bgmethod1 last:
Note that the --bgmethod0 and --bgmethod1 options
only affect the ChangeLog method, and only when motion blur is not used.
Blur length 1:
Blur length 4:
Blur length 20:
Produced with commandline:
# for b in 1 4 20;do
# rm tile-*.png tile-*.gif
# animmerger --gif -B$b -pc snaps/*.png -m0,8,256,16,020202,A64010,D09030,006E84,511800,FFFFFF
# gifsicle -O2 -o demo/method-cB"$b".gif -l0 -d3 tile-*.gif
# done
-l
option
to set the loop length in frames.
30 frames (94895 bytes):
10 frames (66738 bytes):
4 frames (40372 bytes):
Produced with commandline:
# for l in 4 10 30; do
# rm tile-*.png tile-*.gif
# animmerger --gif -l$l -po snaps/*.png -m0,8,256,16,020202,A64010,D09030,006E84,511800,FFFFFF
# gifsicle -O2 -o demo/method-sl"$l".gif -l0 -d3 tile-*.gif
# done
It is also called "loopinglast" mode (option -s) to differentiate from "loopingavg".
The loopinglog method also supports motion blur. Use the --motionblur (-B) option to set it. Value 0 disables motion blur (default: 0).
-l
option to set the loop length in frames.
30 frames (file size depends on selected palette size):
10 frames:
4 frames:
Produced with commandline:
# for l in 4 10 30 80; do
# rm tile-*.png tile-*.gif
# animmerger --gif -l$l -pv snaps/*.png -m0,8,256,16,020202,A64010,D09030,006E84,511800,FFFFFF
# gifsicle -O2 -k128 -o demo/method-ov"$l".gif -l0 -d3 tile-*.gif
# done
If you want the color averages to be calculated through the YUV colorspace
rather than the RGB colorspace, add the --yuv option.
In most cases, the difference is neglible though.
Loop length 30 frames, blur length 20:
Loop length 30 frames, blur length 20, with YUV calculations:
Loop length 30 frames, blur length 20, with YUV calculations, and diversity-quantized palette of 16 colors:
Loop length 10 frames, blur length 4:
Produced with commandline:
# for b in 4 8 20;do
# for l in 10 30;do
# rm tile-*.png tile-*.gif
# animmerger --gif -B$b -l$l -pl snaps/*.png -m0,8,256,16,020202,A64010,D09030,006E84,511800,FFFFFF
# gifsicle -O2 -o demo/method-vl"$l"B"$b".gif -l0 -d3 tile-*.gif
# done
# done
Method name | Static or animated |
Composes new colors |
Obeys YUV option |
Memory size per pixel* | Primary use |
---|---|---|---|---|---|
First | Static | No | No | 4 | Maps |
Last | Static | No | No | 4 | |
FirstNMost | Static | No | No | As ChangeLog | |
· FirstUncommon | Static | No | No | As ChangeLog | |
· FirstNLeast | Static | No | No | As ChangeLog | |
LastNMost | Static | No | No | As ChangeLog | |
· LastUncommon | Static | No | No | As ChangeLog | |
· LastNLeast | Static | No | No | As ChangeLog | |
MostUsed | Static | No | No | 12…16 + 6×number of unique colors | Maps |
LeastUsed | Static | No | No | As MostUsed | |
Solid | Static | No | No | 12 | Maps |
Average | Static | Yes | Yes | 16 | |
Tinyaverage | Static | Yes | No | 8 | |
ActionAvg | Static | Yes | Yes | As MostUsed | |
ChangeLog | Animated (movie) | If blur | For blur | 12…16 + 8×number of content changes | Animations |
LoopingLog | Animated (loop) | If blur | For blur | As ChangeLog | |
LoopingAvg | Animated (loop) | Yes | Yes | As ChangeLog | Fun |
*) These numbers are estimates. Actual memory size per pixel depends on the exact selection of pixel methods requested and the memory allocation overhead. Animmerger strives to always select the smallest combination of pixel methods (memoryconsumptionwise) that can implement all the requested methods.
These images were produced with this commandline:
# for method in censor hole interpolate extrapolate; do
# rm *-*.gif *-*.png
# ./animmerger -r4,4 --mvrange 0,0,4,0 --bgmethod0=first --bgmethod1=last \
# -u$method -p* snaps/*.png \
# -m0,8,256,16,020202,A64010,D09030,006E84,511800,FFFFFF \
# -m3,128,250,72 -m0,73,256,2
# gifsicle -O2 -o demo/mask-$method.gif -l0 -d3 ChangeLog-*.gif
# cp -p Average-0000.png demo/mask-$method.png
# done
Animation:
Averaged:
Animation:
Averaged:
Animation (palette-reduced and dithered with -Qd,16 in order to make the 1.5 MB GIF file smaller):
Averaged:
Note that this algorithm is rather slow on large areas like this.
Animation:
Averaged:
The images in this section were generated by making a 30-frame LoopingAvg animation with blur length of 20, rendering it with different palettization parameters and picking the 11th frame.
The exact commandline to produce the images was:
# for m in m d b o q; do
# for q in 2 4 8 16 32 64 128; do
# rm tile-*.png tile-*.gif
# dither="-Dy2 --dr 2 --dc 16"
# if [ $q -gt 16 ]; then dither="-Dy1i --dr 1.2 --dc 16"; fi
# animmerger --gamma 2.2 $dither \
# --gif -Q"$m",$q -B20 -l30 -pv snaps/*.png \
# -m0,8,256,16,020202,A64010,D09030,006E84,511800,FFFFFF
# gifsicle -O2 -k128 -o demo/method-vl30yB20Q"$m"$q.gif -l0 -d3 tile-*.gif
# convert tile-0010.gif -quality 100 demo/quant-"$m"$q.png
# done
# done
Palette reduction methods can be chained in order to take benefits of the differently-appearing strengths of the different methods, but in this test set, each method was used alone.
When palette reduction methods have been explicitly selected, animmerger always uses an ordered-dithering method (crosshatch artifacts) to optimize the rendering. This is better for animation than other methods such as Floyd-Steinberg are, because the dithering patterns do not jitter between frames.
4 colors:
8 colors:
16 colors:
32 colors:
4 colors:
8 colors:
16 colors:
32 colors:
4 colors:
8 colors:
16 colors:
32 colors:
4 colors:
8 colors:
16 colors:
32 colors:
Animmerger knows a number of different dithering algorithms, including a set of dithering algorithms devised by Joel Yliluoma. These algorithms are categorised as an ordered, positional, patterned dithering method that is very well suited for animations, and often even more eye-pleasing than the random noise patterns generated by error diffusion dithers.
Animmerger's dithering can be controlled with the following parameters:
# for gamma in 0.1 0.2 0.5 1.0 1.5 2.0 2.2 2.5 3.0 10.0; do
# animmerger gamma_in.png -Q../cga0-pal.gif --dm 8x8 --dc 64 --gamma $gamma
# mv tile-0000.png demo/gamma-$gamma.png
# done
Note that animmerger's gamma correction algorithm is somewhat disputable. For instance, although the former example looks good, if we try the same with the EGA palette, where mid-gradient values (0%, 33%, 66% and 100% white) actually exist in the palette, we get the latter, odd-looking result.
Conclusion: Your mileage may vary.
It is a subset (cropped portion) of a larger picture seen on the page where the algorithm is explained in detail, hence the odd inclusion of blue in it.
The error spread factor provides a very fine-grained control over the final appearance of the dithered image. Though the upper limit of the value is 1.0, higher values can be used for artistic purposes.
The matrix shape directly controls the manner in which the different-color spots are dispersed. The temporal dithering option can be used for improving the perceived quality of colors (at the cost of flickering), and for artistics effects.
Unless the --dithcount (--dc) option was given manually, setting the matrix size also sets the former. (To the size of matrix, or 32, whichever is smaller.)
Note that when making GIF animations, you usually do not want flickering,
because it will inflate the file sizes at a very high rate. With H.264,
it is perfectly fine, especially if you use the frameref
setting. (No, Animmerger does not have H.264 output. I was just referring
to the hypothetical scenario that you would use animmerger to create
a video that will be encoded in H.264.)
The candidate count option directly controls how colors are mixed together in the dithering process. A higher value always results in higher quality, however, there is no sense in making the value larger than the matrix size is. Also, a combination of a large matrix and a small count can be used to simulate a small dithering matrix.
Also note that the rendering speed is directly proportional to the number of dither color candidates generated. (It also depends on the size of the palette of both input and output images, and on the dither contrast limiter.)
Specifying 0 for the contrast usually works nicely, especially if the palette is good, but sometimes you will have to put a higher value there. Such situations may happen if the palette contains a combination of colors that produces the exact color required in the input picture when mixed, but also a closeby color that is not exact. Without the aid of the contrast option, the ditherer will not find the combination and will just use the closerby color that might not look as good. Overdoing it, however, will result in a lot of overly sharp local contrast, which looks mostly bad. Animation is shown in the last frame for the sake of demonstration, because it improves the spatial color resolution.
Note that using nonzero --dr with --gamma that differs from 1.0 is currently broken. Please avoid that combination.
Here are two example truecolor* pictures, and the
web-safe palette.
I quantized it using the websafe palette and dithered using
--dm 1x1 --dc 1 --dr 0
(i.e. no dithering),
and varied the color compare method using the --deltae
option.
These tests intend to show how each color-compare method identifies colors that most closely match the original. Note: I used gamma correction for these images. Consequently, I disabled the --dr option because these do not mix well together.
Produced with commandline:
# for e in rgb cie76 cie94 cmc ciede2000 bfd; do
# # Render the chroma&luma test image without dithering:
# animmerger deltae_base.png -Qdeltae_pal.png -vv \
--dm=1x1 --dc=1 --dr=0 --deltae=$e --gamma 2.0
# mv tile-0000.png demo/deltae_$e.png
#
# # Create four-frame temporal-dithered animation of the testcard:
# animmerger tksmall{,,,}.png --noalign -Qdeltae_pal.png -vv \
--dm 1x1,-4 --dc 4 --deltae=$e -pc --gif --gamma 2.0
# gifsicle -O2 -o demo/tk-$e.gif -l0 -d4 tile-000[0-3].gif
#
# # Create an average of those four frames:
# animmerger tile-000[0-3].gif -pa --noalign
# mv tile-0000.png tk-a4-$e.png
# done
*) It is truecolor, but it is also dithered. I found 24-bit RGB inadequate for this picture in preventing hard edges in smooth gradients, so I dithered it for this webpage. The input to these tests was undithered.
RGB. Calculated as a simple euclidean difference:
√(ΔR² + ΔG² + ΔB²)
It is very fast and does not usually cause any nasty surprises.
However, in dithering, it usually is overly conservative and fails
to account for psychovisuals, i.e. when some color is "close enough",
and consequently, fails to achieve a pleasing result.
Note: Animmerger uses the deltaE squared rather than the deltaE itself,
which is why the formula may seem different to what it is in reference
material. (There may still be genuine errors though.)
Animmerger knows three illuminant matrices:
CIE E illuminant:0.488718, 0.176204, 0.000000 0.310680, 0.812985, 0.0102048 0.200602, 0.0108109, 0.989795 |
Adobe D65 illuminant:0.576700, 0.297361, 0.0270328 0.185556, 0.627355, 0.0706879 0.188212, 0.0752847, 0.99124 |
Unidentified D65-based illuminant, found in Imagemagick:0.412453, 0.357580, 0.180423 0.212671, 0.715160, 0.072169 0.019334, 0.119193, 0.950227 |
Animmerger uses illuminant #3 for CIE76, and illuminant #1 for all other CIE based compare methods, because illuminants #2 and #3 have serious issues with blue and purple tones when any other compare method than CIE76 is used (specifically, they suggest that black is the overwhelmingly best substitute for those colors).
Animmerger converts a RGB value into
CIE L*a*b* and CIE L*C*h*
using the following formula:
X = (i0 × R + i3 × G + i6 × B) ÷ 255 ÷ (i0+i1+i2)
Y = (i1 × G + i4 × G + i7 × B) ÷ 255 ÷ (i3+i4+i5)
Z = (i2 × B + i5 × G + i8 × B) ÷ 255 ÷ (i6+i7+i8)
f(v) = v ≤ 6329−3 ? 4÷29 + v × 2926−23−1 : v1÷3
L = 4×29 × f(Y) - 42
a = 500 × (f(X) - f(Y))
b = 200 × (f(Y) - f(Z))
C = √(a² + b²)
h = atan2(b, a)
Where i0…i8 are the values from the illuminant matrix.
--transform
option.
In this example, the overall color tone of the image was changed
and a lens flare effect was added:
Produced with:
# ./animmerger --gif snaps/*.png -m0,8,256,16,020202,A64010,D09030,006E84,511800,FFFFFF \
# -pv -l25 --deltae=2000 -Qq,36 -Qd,32 --dr 0 --dm 8x8 --dc 32 -Dky -vv \
# --transform "luma:=r*.299+g*.587+b*.114; px:=238;py:=135;" \
# --transform "xy:=(abs(x-px)*abs(y-py)/200^2);" \
# --transform "rad:=hypot(x-px,y-py)^1.3/(1.8+0.02*cos(((frameno/25+.5)%1)^2*-2*pi));" \
# --transform "v:=luma+(300-min(300,rad))+(200-min(300,(xy+1e-6)^0.6*7e3));" \
# --transform r="r*v/(255*0.299*2.2)" \
# --transform g="g*pow(v,1.2)/(255*0.587*3.1)" \
# --transform b="b*v/(255*0.114*1.7)+50*(1.35+cos(1+(y+x*.2)*pi/100))"
# gifsicle -O2 -o demo/trans.gif -d3 -l0 tile-????.gif
If different background layers are moving at different speeds with respect to the camera, animmerger will sync into one of them (probably the one that occupies the largest screen area), and the rest will appear to be moving with respect to the chosen background.
Example:
This scene is from Super Mario World. The HUD layer is disabled, but otherwise it is an intact animation. The palette was reduced and fps halved to make the file slightly smaller for web distribution. | |
![]() |
![]() |
You can easily see the problem with parallax motion: Sometimes the image alignment syncs to the platforms, sometimes it syncs to the stalactite background. When it syncs to the platforms, the other background can be seeing moving as a distinct layer. |
In this version, the background layers move in unison with respect
to the camera. As such, the image alignment is perfect. This was achieved with the following patch to snes9x: --- ppu.cpp~ 2010-08-17 23:46:11.022843689 +0300 +++ ppu.cpp 2010-08-17 22:34:52.000000000 +0300 @@ -416,2 +416,3 @@ PPU.BG[0].HOffset = (Byte<<8) | PPU.BGnxOFSbyte; + PPU.BG[1].HOffset = (Byte<<8) | PPU.BGnxOFSbyte; PPU.BGnxOFSbyte = Byte; @@ -423,2 +424,3 @@ PPU.BG[0].VOffset = (Byte<<8) | PPU.BGnxOFSbyte; + PPU.BG[1].VOffset = (Byte<<8) | PPU.BGnxOFSbyte; PPU.BGnxOFSbyte = Byte; @@ -429,3 +431,3 @@ case 0x210F: - PPU.BG[1].HOffset = (Byte<<8) | PPU.BGnxOFSbyte; + //PPU.BG[1].HOffset = (Byte<<8) | PPU.BGnxOFSbyte; PPU.BGnxOFSbyte = Byte; @@ -436,3 +438,3 @@ case 0x2110: - PPU.BG[1].VOffset = (Byte<<8) | PPU.BGnxOFSbyte; + //PPU.BG[1].VOffset = (Byte<<8) | PPU.BGnxOFSbyte; PPU.BGnxOFSbyte = Byte; |
# rm tile-*.gif; animmerger -v -r12x12 -bl -pc -a -4,-3,6,9 pano5/*.png
# rm tile-???[13579].gif # Delete 50% of frames to reduce fps in half
# gifsicle -O2 -o demo/pano5-cl.gif --crop 8,8+480x400 --use-colormap smwpalette.gif -l0 -d6 tile-????.gif
# rm tile-*.gif; animmerger -v -r12x12 -bl -pc -a -4,-3,6,9 pano5b/*.png
# rm tile-???[13579].gif # Delete 50% of frames to reduce fps in half
# gifsicle -O2 -o demo/pano5-fl.gif --crop 8,8+480x400 --use-colormap smwpalette.gif -l0 -d6 tile-????.gif
The palette file was customized by hand, by taking a representative snapshot of the movie, and then progressively merging near-identical entries in the colormap in GIMP manually until only the minimal set of unique colors/tones remain.
Example:
TODO: Add successful Super Metroid animation
TODO: Add example of how image alignment suffers when using the power bomb in Super Metroid
animmerger v1.6.1 - Copyright (C) 2013 Joel Yliluoma (http://iki.fi/bisqwit/) Usage: animmerger [<options>] <imagefile> [...] Merges (stitches) animation frames together. General options: --help, -h Short help on usage. Use --longhelp or --fullhelp for more/all options. Canvas affecting options: --method, -p <mode> Select pixel type (average/actionavg/mostused/changelog/loopingavg) See full help for details. --looplength, -l <int> Set loop length for the LOOPINGxx modes Image aligning options: --noalign Disable automatic image aligner. Useful if you only want to utilize the dithering and quantization features of animmerger, or your images simply don't happen to form a nice 2D map. This option is identical to --forcealign 0-N=0,0 where N=movie length. Output options: --output, -o <filename/pattern> Output to given filename. The filename may also be a pattern. (See longhelp for details.) --quantize, -Q <method>,<num_colors> or <file> or <R>x<G>x<B>[x<I>] Reduce/load/synthesize palette. See full help for details. --dithmethod, -D <method>[,<method>] Select dithering method(s) (ky/y2/floyd). See full help for details. --gamma [=<value>] Select gamma to use in dithering. Default: 1.0
Additionally, the most recent source code (bleeding edge) for animmerger can also be downloaded by cloning the Git repository by:
git clone git://bisqwit.iki.fi/animmerger.git
git checkout origin/release -b release
git checkout origin/master -b master
Generated from
progdesc.php (last updated: Sat, 15 Aug 2015 15:40:47 +0300)
with docmaker.php (last updated: Sat, 15 Aug 2015 15:40:47 +0300)
at Sat, 15 Aug 2015 15:40:48 +0300