I've managed to pull off a number of helloworldlets:


The ones in large boldface appear in the final production. Progressively sillier program names are a byproduct of my homegrown version-control algorithm...


hc/lib/loadj.c
Figuring out libjpeg well enough to read in a color jpeg image to play with and displaying it with GL (writing too, now, and color or grayscale)
dd/dc.c
drawing Dirichlet domains in GL (with z-buffered cones)
dd/djt.c
and making a mosaic out of an image and the DD's (anything to get rid of the grid...)
hc/lib/color.c
Color space conversion. Everything here was done in YIQ space.
hc/lib/wto.c
wavelet-transforming images (DAUB4 and Haar wavelets)
wav/jwdemo.c, wav/sigrun.c
Two (Daubechies-4) wavelet-compressing demos: in the first, you can tweak thresholds for Y,I,Q coefficients (dreadful glutMenuThing interface); the second shows the image reconstructed from only its biggest coefficient, then adds the next largest, ...
hc/lib/top.c
Finding the top M of N wavelet coefficients (cross between heap-select and index sort), zeroing all the rest, and transforming back (99%+ can go away and look all right, or far more and still be of some use). By the way, it turns out that only rarely are more than two or three of the top40 coeffs in the I or Q direction.
hc/cropsqs.c
Image cropping: as many as can fit of as large as possible of 2n x 2n chunks; used for making the biggest tiles from random jpegs.
hc/lib/rez.c
Image scaling: rescale a 2n x 2n image up or down by factors of two, with no bicubic resampling or anything; scaling used not for matching at different resolutions but only for applying tiles at the right size (early revs use glRasterPos(), glPixelZoom(), and glPixelDraw() to actually draw them on in the right place at the right size, and then glReadPixels() to see what it is that you've drawn: SLOW! -- now, just resize the pixel data with rez() and write it into the right subsquare of the array).
hc/lib/tile.h
Repository of declarations of the data structures that crop up throughout.
hc/targa.c
DAUB4 wavelet pyramid scheme on a target image (top40 coefficients and average Y for subsquares). The wavelet transform is run one level at a time, and each intermediate stage is yanked out and translated into the wavelet transforms of that level of subsquares. The datafile produced contains a {filename, size, nnodes} structure followed by the an nnodes-element array of {average subsquare luminance, top40 coeffs, size} in "for (level), for(x), for(y)" order, 4-ary-tree order. Result: matches() can match tiles against any subsquare in this iceberg.
hc/tirade.c
DAUB4 wavelets (top40 coefficients and average Y) for potential tile images. Run it batchly on everything in a directory; run a post-matches (snatches, sneetches) program snatches on the cat of any individual tile.dat files you want to use. (Caveat: this currently records only file basename, not path, so matches and triage need to be run in the directory of the tiles and the tilefile, and not really on the cat of tilefiles that refer to files in more than one directory.) The approximation is that the wavelet coefficients of a half-resolution (etc.) version of a tile are the low-frequency subset of those for the full-res image, and that those for the double-resolution version are the full-res ones plus a lot of zeroes. With Haar wavelets, these are exact; with DAUB4's, you lose a factor of twoish with each rescaling. The same matching algorithm (find which tiles have the most coefficient sign matches among their lists of top 40 coefficients) applies for any size tile against any size target subsquare.
hc/testtirade.c
Shows how much information is left in those top40 coefficients: does the inverse wavelet transform on an array in which everything but them is zeroed and shows the picture.
hc/testtarga.c
Same thing, but given a target file it shows the inverse transform for the whole target image, then each quarter, sixteenth, etc., down to the resolution of the target data file, which has typically been 16x16 pixel squares.
hc/ttest.c
Just turns a target or tile data file into readable ascii, for a check.
hc/tedit.c
It was too close to due-hour to rebuild the database when I realized the kids-in-tutus picture was a REALLY GOOD MATCH for a certain chunk of target and WOULDN'T GO AWAY (same with that NASA logo). Thus was conceived tedit.c, to which you pass any number of filename prefixes, and it copies the database minus any res-sturctures the filenames of which begin with those characters. Kids, Golf, Ballet, Balloons: all gone.
hc/matches.c
Given a target data file (with top-40 wavelet coefficients for 2m x 2m subsquares of the target image down to some fine scale) and a tile data file (with top-40 coeffs for each of many square potential tile images at their original resolution), test each potential tile (at the appropriate resolution, i.e. comparing only the wavelet coefficients that appear at that res.) against each target subsquare, looking for the best scoring match (using the harsh quantization algorithm of the "Multiresolution Image Database Querying" article (let me find that citation...), in which the score is simply the number of wavelet coeffs that appear in the top40 of both tile and target with the same sign(!!), plus a certain weighting of luminance agreement (completely lost in the quantization)). So for each match of tile onto target subsquare, the data recorded is {filename, exp = log2 (magnification), score}.

In the current version, the top two contenders are noted at all times; any upstart that places in the top two is inserted in the right place, ousting one of the previous contenders. A current best match may match with a score of zero, so that there is always at least some skanky match for any square, so triage doesn't freak out.

Look what I learned how to do: Since this takes a little while to run, it's been adjusted to accept a "kill -SIGUSR1 pid" signal, on which it writes the current best matches to a file usable by any post-triage program and exits. It also tells you how many tiles it got through, and can be restarted on the n+1st tile by invoking it in the same manner as before only with that number as an additional argument.

hc/treeage.c
"triage.c" was The first shot at collage/montage/triage code. For now, it just draws the tiles in the right place at the right resolution, using GL, one layer over/obscuring another, getting the exp=log_2(magnification) from the matches datafile and sending the proper magnification to glPixelZoom(). Ridiculously enough, the screen shots of the individual layers that appear on the results page were obtained by running triage in gdb, with a breakpoint set at the appropriate time. (triage.c itself (and matches.c, its partner in crime) is out of date; their datafile structure has been changed.) "treeage" is the same thing plus some optimizations regarding how much data to store (next gen, different file structure). It grabs (glReadPixels()) the pixel data from each layer and adds it (mod modulation, which is also noted in a normalization array) into an accumulation array.
hc/treeedge.c
Another round of collage/montage/triage code. Attempts to deemphasize edges using sin2(x)*sin2(y) modulation (= near 0 around edges of that layer, so lower layers show through more there). For example, at the center of the final image, the only thing that shows well is the only layer that has no edge there, the full-size single-tile lowest-frequency subsquare. This produces the images where the tile content is most visible.
hc/troismenage.c
More collage/montage/triage. Also weights by tile score, and foreground/background-ness of image coordinates.
hc/trog.c
At last, just try a straight average of the layers! Results are embarrassingly similar to those from the score-weighting versions.
hc/crwton.c
Cluster-weighted modeling with expectation maximization: extension of the class assignment to 2D.
hc/trwth.c
A triage-like program, incorporating CWM. Still buggy.


Trying out libjpeg

Drawing a random dirichlet-domain mosaic.

Drawing a dirichlet-domain mosaic based on a source jpeg.

Wavelet compression demo 1: tweak thresholds for Y,I,Q coefficients.

Wavelet compression demo 2: shows picture with only its biggest coefficient, then adds the next largest, ...

c=Y: avg=6.043251, max=15844.109375, zeroed=58330
c=I: avg=0.866628, max=1391.290283, zeroed=56751
c=Q: avg=0.507719, max=103.790565, zeroed=52942
nonzeroed=28585

c=Y: avg=6.043251, max=15844.109375, zeroed=65401
c=I: avg=0.866628, max=1391.290283, zeroed=65514
c=Q: avg=0.507719, max=103.790565, zeroed=65531
nonzeroed=162

c=Y: avg=6.043251, max=15844.109375, zeroed=65468
c=I: avg=0.866628, max=1391.290283, zeroed=65519
c=Q: avg=0.507719, max=103.790565, zeroed=65536
nonzeroed=85

CWM in 2d using the full covariance matrix (4 clusters, gnuplot plot)


ebeth@media.mit.edu