ref: 200239cdda62ba801df9c079a773cca999348a90
dir: /blur.c/
#include "a.h"
enum { Fbox, Fgaussian, };
char* filters[] = { "box", "gaussian" };
double *kernel;
int size;
double boxkernel[] = {
1./9., 1./9., 1./9.,
1./9., 1./9., 1./9.,
1./9., 1./9., 1./9.,
};
double*
computekernel(int radius, double σ)
{
const double Π = 3.14159265358979323846;
double *k, d, g, s;
int kw, x, y;
kw = 2*radius+1;
k = malloc(kw*kw*sizeof(double));
if(k == nil)
sysfatal("malloc: %r");
d = 1.0/(2.0*Π*σ*σ);
s = 0.0;
for(y = -radius; y <= radius; y++){
for(x = -radius; x <= radius; x++){
g = d*exp(-(x*x+y*y)/(2*σ*σ));
k[(x+radius) + kw * (y+radius)] = g;
s += g;
}
}
for(y = 0; y < kw; y++){
for(x = 0; x < kw; x++){
k[x + kw * y] /= s;
}
}
return k;
}
uchar*
convolve(uchar *data, int w, int h, int depth)
{
uchar *out;
int kw, n, x, y, kx, ky;
double v, r, g, b;
if(size < 0)
sysfatal("gaussian filter needs a size argument");
n = depth*w*h*sizeof(uchar);
out = eallocbuf(n);
memmove(out, data, n);
kw = 2*size+1;
for(y = size; y < (h - size); y++){
for(x = size; x < (w - size); x++){
r = g = b = 0.0;
for(ky = -size; ky <= size; ky++){
for(kx = -size; kx <= size; kx++){
v = kernel[(kx + size) + kw * (ky + size)];
#define P(X,Y,C) data[((X) + (w*(Y)))*depth + C]
r += (double)P(x - kx, y - ky, 0) * v;
g += (double)P(x - kx, y - ky, 1) * v;
b += (double)P(x - kx, y - ky, 2) * v;
#undef P
}
}
out[(x + w*y)*depth + 0] = r;
out[(x + w*y)*depth + 1] = g;
out[(x + w*y)*depth + 2] = b;
}
}
return out;
}
void
usage(void)
{
fprint(2, "usage: %s -f box|gaussian [-s size]\n", argv0);
exits("usage");
}
void
main(int argc, char *argv[])
{
char *fname;
int f, i;
fname = nil;
size = 2;
f = -1;
ARGBEGIN{
case 'f':
fname = EARGF(usage());
for(i=0; i<nelem(filters); i++){
if(strcmp(fname, filters[i])==0){
f = i;
break;
}
}
break;
case 's':
size = atoi(EARGF(usage()));
break;
default:
usage();
break;
}ARGEND;
if(f<0)
usage();
switch(f){
case Fbox:
size = 1;
kernel = boxkernel;
break;
case Fgaussian:
kernel = computekernel(size, 1.0);
break;
}
applyfilter(fname, convolve);
}