I’m experimenting with adding different types of animated noise to static images to create an illusion of movement. Nothing very successful so far, but I’m enjoying the periodic modulation of the pixelation effect on this picture of David Foster Wallace. I’m just making a snapshot of this experiment now so that I can return to it later.
Click on the gif below to view it at full size. The original image is included below the code.
//
// pixelator.c - a funny kinda pixelator
// Written by Ted Burke - 11-11-2019
//
// This program loads an image from a PNM file ("dfw1280.pnm"),
// then outputs a series of images, each of which is a "pixelated"
// version of the original. The pixelation process consists of
// iterating through every pixel in the image in random order,
// replacing each one with a dot of the same colour, but of
// random size at the same x,y location. The maximum dot size
// varies from frame to frame.
//
// To compile:
//
// gcc -o pixelator pixelator.c -lm
//
// To combine frames into a single gif using ImageMagick:
//
// convert -loop 0 -delay 12 frame* dfw.gif
//
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define W 1280
#define H 842
// Pixel buffers for input and output images
unsigned char pi[H][W][3];
unsigned char po[H][W][3];
// An array of these stuctures is used to store the image pixels
// in a sortable form. When the order of the pixels is shuffled,
// each pixel in the array remembers its colour and x,y position.
typedef struct pixel
{
int x;
int y;
int z;
unsigned char r;
unsigned char g;
unsigned char b;
} Pixel;
Pixel px[W*H];
// This function is used by qsort to sort the pixels by z order
int zcompare(const void *p1, const void *p2)
{
if (((const Pixel *)p1)->z > ((const Pixel *)p2)->z) return 1;
else if (((const Pixel *)p1)->z < ((const Pixel *)p2)->z) return -1;
return 0;
}
// This function renders a dot of specified size and color at
// the specified location in the output image pixel buffer
void dot(int cx, int cy, int rad, unsigned char r, unsigned char g, unsigned char b)
{
int x, y, rad2;
rad2 = rad*rad; // radius squared
for (y=cy-rad ; y<=cy+rad ; ++y) for (x=cx-rad ; x<=cx+rad ; ++x)
{
if (y>=0 && y<H && x>=0 && x<W && ((x-cx)*(x-cx)+(y-cy)*(y-cy))<=rad2)
//if (y>=0 && y<H && x>=0 && x<W) // square dots
{
po[y][x][0] = r;
po[y][x][1] = g;
po[y][x][2] = b;
}
}
}
int main()
{
int t, n, x, y, T=32;
char buf[1024];
char filename[256];
const char input_filename[] = "dfw1280.pnm";
FILE *f;
// Read image from file
fprintf(stderr, "Reading %s...", input_filename);
f = fopen(input_filename, "r");
for (n=0 ; n<3 ; ++n) fscanf(f, "%[^\n]\n", buf); // PNM header
fread(pi, 3, W*H, f); // pixel data
fclose(f);
fprintf(stderr, "done\n");
// Randomise pixel order
for (y=0 ; y<H ; ++y) for (x=0 ; x<W ; ++x)
{
n = y*W + x;
px[n].x = x;
px[n].y = y;
px[n].z = rand(); // assign random z order
px[n].r = pi[y][x][0];
px[n].g = pi[y][x][1];
px[n].b = pi[y][x][2];
}
for (t=0 ; t<T ; ++t)
{
// Shuffle z order of pixels
for (n=0 ; n<W*H ; ++n) px[n].z = rand();
qsort(px, W*H, sizeof(Pixel), zcompare);
// Modify image
for (n=0 ; n<W*H ; ++n)
{
// Draw a random sized dot at the current pixel location
dot(px[n].x, px[n].y, rand()%((int)(7.0+2.0*sin(t*2.0*M_PI/T))), px[n].r, px[n].g, px[n].b);
}
// Write image to file
sprintf(filename, "frame%03d.pnm", t);
fprintf(stderr, "Writing %s...", filename);
f = fopen(filename, "w");
fprintf(f, "P6\n%d %d\n255\n", W, H);
fwrite(po, 3, W*H, f);
fclose(f);
fprintf(stderr, "done\n");
}
}
The program requires the input image in PNM format. I converted the above PNG image to PNM format using ImageMagick’s convert command:
convert dfw1280.png dfw1280.pnm

