First, the movie rip. IM has serious problems trying to grab frames from a large movie file. However, this is because it simply calls ffmpeg to grab the entire movie as an uncompressed file. You can just modify the middleman step yourself by using ffmpeg to output a PAM file:
Code: Select all
ffmpeg -ss 00:12:30 -i movie.wmv -r 8 -vframes 32 -vbsf remove_extra -an -vcodec pam -f rawvideo -y anim.pam
Second, you will probably want to resize the frames to a smaller size. Something like 1280x768 ain't going to work on animated GIF. How you resize it is critically important, so I'll tell you the best method:
Code: Select all
convert anim.pam -depth 32 -colorspace YCC -resize 300x169! -colorspace Lab -identify anim.miff
Why YCC? After a bunch of research on all of the different colorspaces, I've found that YCC provides the lowest color usage ratio for dither operations. This means that the ordered dither can achieve much higher posterization levels than with other colorspaces. Higher posterization levels means much more colors to choose from in the dither, and more importantly, a higher luminance:hue ratio. (More on that in a bit.)
Why Lab? Lab is the preferred colorspace for color quantization and dithering because it is perceptually uniform, ie: a change of the same amount in a color value should produce a change of about the same visual importance to the human eye. Compare this to RGB, where G=255 looks the brightest and B=255 looks not as bright (because our eyes can see green "better"). Since a dither operation is about picking colors with a similar value to each other, Lab dithers will blend in much better.
Why MIFF? Because IM's own format is the only one that would be able to store an animation of frames beyond 256 colors. Remember, we still need to dither before we get that low in the color count.
The hard part will be the ordered-dither guessing process. Since I'm doing this via code, I'll put some of this in a pseudo-code format. Note that I have various shortcuts in place to make the process faster.
A few things to know before we get into it, though. First, the L in Lab is luminance and the AB are the colors. (They call AB "color-opponent dimensions", but the important thing is that AB holds all of the hue/saturation type values and that you really can't separate AB without getting really weird results.) Second, the human eye can see differences in luminance much better than differences in color. So, while the color levels should still be an a certain minimum, the luminance levels should be as high as we can get them.
The bare minimum dither should be 15,11,11. That gives 15 levels of luminance and 11*11=121 different colors, plus the dithering itself which "fakes" other hues. For my uses, I've found that AB levels from 11 to 13 are best. Anything higher than that is wasting what little color space GIF has, instead of favoring luminance more. The goal here, like it was with the plane.avi demo in the IM guide, is to get 255 colors (saving one for transparency).
Also, I'm using three ordered patterns: o8x8, o4x4, and o2x2. The former is the best, but the latter can work well if you need to use less byte size.
Code: Select all
thresholds = (o8x8 o4x4 o2x2)
od = shift thresholds // in other words, od = o8x8
ab_max = 13
for l in (15 .. 100) {
for ab in (11 .. ab_max) {
lvl_str = join ',', (od l ab ab)
ttl_colors = l * ab * ab
cratio = min_cratio{od+','+ab} || min_cratio{od+',11'} || min_cratio{'o8x8,11'}
if (ttl_colors >= cratio * 250) { // ratio is too low to hit 255 colors, so don't waste time with the query
open ID "| convert anim.miff -ordered-dither "+lvl_str+" -depth 8 -colorspace sRGB -append -format %k info:")
num_colors = int(<ID>)
close(ID)
cratio = ttl_colors / num_colors
min_cratio{od+','+ab} = min(cratio, min_cratio{od+','+ab} || 99999)
}
else num_colors = int(ttl_colors / cratio)
if (num_colors > 255) { ab_max = ab-1; last; } // too many colors; bounce out and don't use that AB level again
else if (num_colors < 200) ab = 13 // too shallow to use; set to AB=13 when adding the levels
// add all of the levels that work, since lower thresolds will have lower # of colors
for new_od in (od, thresholds) {
lvl_str = join ',', (new_od l ab ab)
push levels, lvl_str
}
got_result = 1
last if (num_colors >= 250) // close call; don't push it...
}
if not got_result {
od = shift thresholds
last if not od
l--; ab_max = 13;
}
}
Code: Select all
convert anim.miff -ordered-dither o8x8,15,11,11 -depth 8 -colorspace sRGB -append -format %k info:
Why sRGB? Well, first, the ordered-dither is currently playing around in the Lab colorspace, so we need to convert it back to a RGB-like format for the final animation. Second, sRGB is especially made for monitors, cameras, smart phones, and other digital equipment. Since the GIF is going to be shown on a monitor, this is the best colorspace to use. Also, sRGB will subtract out the really dark ranges that RGB has, since there tends to be a lot of "unusable" colors in that range. (Can you really see something like B=10?) This means more posterization levels and more "usable" colors.
The final convert line is:
Code: Select all
convert anim.miff -ordered-dither o8x8,25,12,12 -depth 8 -colorspace sRGB +dither +remap -coalesce -deconstruct -layers RemoveDups -layers Optimize -delay 1x9 -identify anim.gif
Notice that this command will remap the colors to a global color table. If you are stitching different scenes together, you will want to process the GIFs separately and use a simple "convert *.gif combined.gif" command to bind them together. This will maintain a global color table for each individual GIF/scene, but not a shared one among the whole file.