To avoid floating point rounding issues, I process the pixels in integer space. For each channel (R,G,B) I create an 1D array of integers (one integer for each pixel) and initialize them to zero.
smaxPixels = width*height; srmap = new int[smaxPixels]; sgmap = new int[smaxPixels]; sbmap = new int[smaxPixels]; for (int i = 0; i < smaxPixels; ++i) { srmap[i] = 0; sgmap[i] = 0; sbmap[i] = 0; }
For each pixel in each image, I add the pixel value to the sum for the corresponding pixel (in the below code, 'offset' is typically zero).
for (int i = 0; i < smaxPixels; ++i) {
int px = sImage.pixels[i];
srmap[i] += red(px);
sgmap[i] += green(px);
sbmap[i] += blue(px);
}
I then produce a normalized image by finding the minimum and maximum components, and mapping all the pixel sums to that range.for (int i = 0; i < smaxPixels; ++i) { minValue = min(minValue, srmap[i]); maxValue = max(maxValue, srmap[i]); minValue = min(minValue, sgmap[i]); maxValue = max(maxValue, sgmap[i]); minValue = min(minValue, sbmap[i]); maxValue = max(maxValue, sbmap[i]); }
pg.beginDraw(); // pg is an offscreen Image buffer I am going to draw into...
pg.loadPixels();
for (int i = 0; i < smaxPixels; ++i)
{
if (maxImagesPerTile == 1)
pg.pixels[i] = color(srmap[i],sgmap[i],sbmap[i]);
else
pg.pixels[i] = color( map(srmap[i],minValue,maxValue,0,255),
map(sgmap[i],minValue,maxValue,0,255),
map(sbmap[i],minValue,maxValue,0,255));
}
pg.updatePixels();
pg.endDraw();
That's the essence of it. The bright "orange" is a normalization effect. Note that if I were to simply "average" the images without the normalization step, the resulting color is generally a dirt-brown. Normalizing the image has the effect of increasing the saturation and the contrast, without affecting the hue in an HSL sense. I discuss this in my paper, although the Atlantic article omitted this important point.