shithub: ifilter

Download patch

ref: ba413ddffe893bcaac763e1c08920c0312a20b33
parent: 84b72ac10d75b4b93b9149ec1d3cbb9984ad533c
author: phil9 <telephil9@gmail.com>
date: Thu Dec 8 14:42:57 EST 2022

blur: code factoring

	box blur is like gaussian blur but uses a non-weighted convolution
	matrix instead so there's no use to have two functions.
	The kernel computation is split from the actual convolution allowing
	to use an hard-coded kernel for box blur and thus the same function.

--- a/blur.c
+++ b/blur.c
@@ -1,54 +1,51 @@
 #include "a.h"
-	
-typedef struct Filter Filter;
 
-struct Filter
-{
-	const char *name;
-	filterfn filter;
-};
+enum { Fbox, Fgaussian, };
+char* filters[] = { "box", "gaussian" };
 
+double *kernel;
 int size;
 
-uchar*
-box(uchar *data, int w, int h, int depth)
+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 σ)
 {
-	uchar *out;
-	int n, x, y, c, s;
+	const double Π = 3.14159265358979323846;
+	double *k, d, g, s;
+	int kw, x, y;
 
-	n = depth*w*h*sizeof(uchar);
-	out = eallocbuf(n);
-	memmove(out, data, n);
-	/* box blur is not defined for edge points */
-	for(y = 1; y < h - 1; y++){
-		for(x = 1; x < w - 1; x++){
-			for(c = 0; c < 3; c++){ /* R G B */
-#define P²(X,Y,C) (data[((X) + w*(Y))*depth + C] * data[((X) + w*(Y))*depth + C])
-				s = P²(x - 1, y + 1, c) +
-					P²(x + 0, y + 1, c) +
-					P²(x + 1, y + 1, c) +
-					P²(x - 1, y + 0, c) +
-					P²(x + 0, y + 0, c) +
-					P²(x + 1, y + 0, c) +
-					P²(x - 1, y - 1, c) +
-					P²(x + 0, y - 1, c) +
-					P²(x + 1, y - 1, c);
-				out[(x + w * y) * depth + c] = sqrt(s / 9);
-#undef P²
-			}
+	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;
 		}
 	}
-	return out;
+	for(y = 0; y < kw; y++){
+		for(x = 0; x < kw; x++){
+			k[x + kw * y] /= s;
+		}
+	}
+	return k;
 }
 
 uchar*
-gaussian(uchar *data, int w, int h, int depth)
+convolve(uchar *data, int w, int h, int depth)
 {
-	const double E = 2.7182818284590452354;
-	const double Π = 3.14159265358979323846;
 	uchar *out;
-	int kw, x, y, kx, ky;
-	double *k, σ, s, e, n, d, v, r, g, b;
+	int kw, n, x, y, kx, ky;
+	double v, r, g, b;
 
 	if(size < 0)
 		sysfatal("gaussian filter needs a size argument");
@@ -55,33 +52,13 @@
 	n = depth*w*h*sizeof(uchar);
 	out = eallocbuf(n);
 	memmove(out, data, n);
-	σ = (double)size / 2.0;
-	kw = 2 * size + 1;
-	k = malloc(kw*kw*sizeof(double));
-	if(k == nil)
-		return nil;
-	s = 0.0f;
-	for(y = -size; y <= size; y++){
-		for(x = -size; x <= size; x++){
-			n = (double)(-(x * x + y * y));
-			d = 2 * σ * σ;
-			e = pow(E, n / d);
-			v = e / (2 * Π * σ * σ);
-			k[(x + size) + kw * (y + size)] = v;
-			s += v;
-		}
-	}
-	for(y = 0; y < kw; y++){
-		for(x = 0; x < kw; x++){
-			k[x + kw * y] /= s;
-		}
-	}
+	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 = k[(kx + size) + kw * (ky + size)];
+					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;
@@ -94,7 +71,6 @@
 			out[(x + w*y)*depth + 2] = b;
 		}
 	}
-	free(k);
 	return out;
 }
 
@@ -105,26 +81,21 @@
 	exits("usage");
 }
 
-static Filter filters[] = {
-	{ "box", box },
-	{ "gaussian", gaussian },
-};
-
 void
 main(int argc, char *argv[])
 {
-	Filter *f;
 	char *fname;
-	int i;
+	int f, i;
 
+	fname = nil;
 	size = 2;
-	f = nil;
+	f = -1;
 	ARGBEGIN{
 	case 'f':
 		fname = EARGF(usage());
 		for(i=0; i<nelem(filters); i++){
-			if(strcmp(fname, filters[i].name)==0){
-				f = filters+i;
+			if(strcmp(fname, filters[i])==0){
+				f = i;
 				break;
 			}
 		}
@@ -136,7 +107,16 @@
 		usage();
 		break;
 	}ARGEND;
-	if(f==nil)
+	if(f<0)
 		usage();
-	applyfilter(f->name, f->filter);
+	switch(f){
+	case Fbox:
+		size   = 1;
+		kernel = boxkernel;
+		break;
+	case Fgaussian:
+		kernel = computekernel(size, 1.0);
+		break;
+	}
+	applyfilter(fname, convolve);
 }