shithub: imagetools

Download patch

ref: fc8a7899256f517ee57df789abb880592cdfe6c8
parent: 01698b29669f3339a7289e0d726f8a6e30d07297
author: rodri <rgl@antares-labs.eu>
date: Fri May 30 20:59:28 EDT 2025

initial work on lánczos resampling and filtering

--- a/convolution.c
+++ b/convolution.c
@@ -23,7 +23,7 @@
 {
 	Biobuf *bin;
 	double *kern;
-	char *line, *f[10];
+	char *line, *f[100];
 	int nf, i, j;
 
 	bin = Bfdopen(fd, OREAD);
@@ -118,8 +118,7 @@
 static uchar
 sample(Memimage *i, Point p, int off)
 {
-	if(p.x < i->r.min.x || p.y < i->r.min.y
-	|| p.x >= i->r.max.x || p.y >= i->r.max.y)
+	if(!ptinrect(p, i->r))
 		return 0;	/* edge handler: constant */
 	return *(byteaddr(i, p) + off);
 }
--- /dev/null
+++ b/filter/bakelanczos
@@ -1,0 +1,113 @@
+#!/bin/rc
+# based on https://github.com/jeffboody/Lanczos
+#
+rfork ne
+
+flagfmt=''
+args='winsize'
+
+if(! ifs=() eval `{aux/getflags $*} || ~ $#* 0){
+	aux/usage
+	exit usage
+}
+
+hoc <<EOF
+a=$1
+
+func sinc(x) {
+	if(x == 0) return 1
+	return sin(PI*x)/PI*x
+}
+
+func withinbounds(x) {
+	if(x > -a && x <= a) return 1
+	return 0
+}
+
+func L(x) {
+	if(!withinbounds(x)) return 0
+	return sinc(x)*sinc(x/a)
+}
+
+func lanczos(x) {
+	if(x == 0) return 1
+	if(x < -a || x >= a) return 0
+	return a*sin(PI*x)*sin(PI*x/a)/(PI^2*x^2)
+}
+
+func lanczos2(x, y) {
+	if(!withinbounds(x) || !withinbounds(y)) return 0
+	return sinc(sqrt(x^2+y^2))*sinc(sqrt(x^2+y^2)/a)
+}
+
+#Δ = 0.1
+#for(j = -a+1; j <= a; j++){
+#for(i = -a+1; i <= a; i++){
+#	if(i > -a+1) print "\t"
+#	w = 0
+##	for(k = -0.5; k <= 0.5; k += Δ){
+##	for(l = -0.5; l <= 0.5; l += Δ){
+##		w += lanczos(i+l)*lanczos(j+k)
+##	}
+##	}
+#	for(k = 0; k <= 1; k += Δ){
+#	for(l = 0; l <= 1; l += Δ){
+#		w += lanczos(i+l)*lanczos(j+k)
+#	}
+#	}
+#	print w*Δ*Δ
+##	print L(i)*L(j)
+#}
+#	print "\n"
+#}
+
+#Δi = 0.5
+#for(j = -a+1; j <= a; j += Δi){
+#for(i = -a+1; i <= a; i += Δi){
+#	if(i > -a+1) print "\t"
+##	print lanczos2(i, j)
+#	print lanczos(i)*lanczos(j)
+#	if(i == a) print "\n"
+#}
+#}
+
+print "co kblack\n"
+print "pe solid\n"
+print "ra -5 -1 5 1\n"
+Δi = 0.01
+for(i = -a+1; i <= a; i += Δi){
+	if(i == -a+1) print "move "
+	if(i > -a+1) print "vec "
+	print i
+	print " "
+	print lanczos(i)
+	print "\n"
+}
+print "co red\n"
+for(i = -a+1; i <= a; i += Δi){
+	if(i == -a+1) print "move "
+	if(i > -a+1) print "vec "
+	print i
+	print " "
+	print L(i)
+	print "\n"
+}
+print "co green\n"
+for(i = -a+1; i <= a; i += Δi){
+	if(i == -a+1) print "move "
+	if(i > -a+1) print "vec "
+	print i
+	print " "
+	print sin(i)
+	print "\n"
+}
+print "co blue\n"
+for(i = -a+1; i <= a; i += Δi){
+	if(i == -a+1) print "move "
+	if(i > -a+1) print "vec "
+	print i
+	print " "
+	print sin(i)*lanczos(i)
+	print "\n"
+}
+EOF
--- /dev/null
+++ b/lanczos.c
@@ -1,0 +1,179 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "fns.h"
+
+static int saturate = 1;
+
+static double
+lanczos(double x, double a)
+{
+	if(x == 0) return 1;
+	if(x < -a || x >= a) return 0;
+	return a * sin(PI*x) * sin(PI*x/a)/(PI*PI * x*x);
+}
+
+static double
+lanczos2(double x, double y, double a)
+{
+	return lanczos(x, a)*lanczos(y, a);
+}
+
+static double
+coeffsum(double *k, int dim)
+{
+	double s;
+	int i, j;
+
+	s = 0;
+	for(j = 0; j < dim; j++)
+	for(i = 0; i < dim; i++)
+		s += k[j*dim+i];
+	return s;
+}
+
+static void
+modulate(double *d, double *s, int dim)
+{
+	int i, j;
+
+	for(j = 0; j < dim; j++)
+	for(i = 0; i < dim; i++)
+		d[j*dim+i] *= s[j*dim+i];
+}
+
+static double
+convolve(double *d, double *s, int dim)
+{
+	modulate(d, s, dim);
+	return coeffsum(d, dim);
+}
+
+static uchar
+sample(Memimage *i, Point p, int off)
+{
+	if(!ptinrect(p, i->r))
+		return 0;	/* edge handler: constant */
+	return *(byteaddr(i, p) + off);
+}
+
+static Memimage *
+resample(Memimage *s, int dx, int dy, int a)
+{
+	Memimage *d;
+	double *k, **im, c, denom;
+	Point imc, sp, dp, cp, p;
+	struct {
+		double x, y;
+	} dp2;
+	int i;
+
+	d = eallocmemimage(Rect(0, 0, dx, dy), s->chan);
+	k = emalloc(2*a*2*a*sizeof(double));
+	im = emalloc(s->nchan*sizeof(double*));
+	for(i = 0; i < s->nchan; i++)
+		im[i] = emalloc(2*a*2*a*sizeof(double));
+	imc = Pt(a, a);
+
+	for(dp.y = d->r.min.y; dp.y < d->r.max.y; dp.y++){
+		dp2.y = (double)dp.y*Dy(s->r)/dy;
+	for(dp.x = d->r.min.x; dp.x < d->r.max.x; dp.x++){
+		dp2.x = (double)dp.x*Dx(s->r)/dx;
+		p = Pt(floor(dp2.x), floor(dp2.y));
+
+		for(cp.y = 1; cp.y <= 2*a; cp.y++)
+		for(cp.x = 1; cp.x <= 2*a; cp.x++){
+			sp = addpt(p, subpt(cp, imc));
+			for(i = 0; i < d->nchan; i++)
+				im[i][(cp.y-1)*2*a + cp.x - 1] = sample(s, sp, i);
+			/* TODO precompute this kernel */
+			k[(cp.y-1)*2*a + cp.x - 1] = lanczos2(dp2.x - sp.x, dp2.y - sp.y, a);
+		}
+
+//static int g;
+//if(g++ == dx*dy/2)
+//fprintm(2, k, 2*a);
+
+		denom = coeffsum(k, 2*a);
+		denom = denom == 0? 1: 1/denom;
+
+		for(i = 0; i < d->nchan; i++){
+			c = fabs(convolve(im[i], k, 2*a) * denom);
+			if(saturate)
+				c = clamp(c, 0, 0xFF);
+			*(byteaddr(d, dp) + i) = c;
+		}
+	}
+	}
+
+	for(i = 0; i < s->nchan; i++)
+		free(im[i]);
+	free(im);
+	free(k);
+
+	return d;
+}
+
+_Noreturn static void
+usage(void)
+{
+	fprint(2, "usage: %s [-s] [-x size] [-y size] [-w window] [file]\n", argv0);
+	exits("usage");
+}
+
+void
+main(int argc, char *argv[])
+{
+	Memimage *in, *out;
+	char *infile, *s;
+	int fd, dx, dy, percx, percy, w;
+
+	infile = "/fd/0";
+	dx = dy = 0;
+	percx = percy = 0;
+	w = 3;
+	ARGBEGIN{
+	case 's':
+		saturate--;
+		break;
+	case 'x':
+		dx = strtoul(EARGF(usage()), &s, 10);
+		if(*s == '%')
+			percx++;
+		break;
+	case 'y':
+		dy = strtoul(EARGF(usage()), &s, 10);
+		if(*s == '%')
+			percy++;
+		break;
+	case 'w':
+		w = strtoul(EARGF(usage()), nil, 10);
+		break;
+	default: usage();
+	}ARGEND;
+	if(argc == 1)
+		infile = argv[0];
+	else if(argc > 0)
+		usage();
+
+	fd = eopen(infile, OREAD);
+	in = ereadmemimage(fd);
+	close(fd);
+
+	if(percx)
+		dx = Dx(in->r)*dx/100;
+	if(percy)
+		dy = Dy(in->r)*dy/100;
+
+	if(dx == 0 || dy == 0)
+		usage();
+	else if(dx == Dx(in->r) && dy == Dy(in->r))
+		out = in;
+	else
+		out = resample(in, dx, dy, w);
+
+	ewritememimage(1, out);
+	exits(nil);
+}
--- a/mkfile
+++ b/mkfile
@@ -9,6 +9,7 @@
 	com\
 	genmask\
 	warp\
+	lanczos\
 
 OFILES=\
 	utils.$O\
binary files /dev/null b/samples/musicsheet.jpg differ
--