ref: d0bb413fab671e82223a64c1011ce5cc738859ac
parent: 936de64a666dbc4284aab5ef364de3f1c38496ec
author: rodri <rgl@antares-labs.eu>
date: Wed Apr 30 19:31:49 EDT 2025
warp: initial work on image warping
--- a/convolution.c
+++ b/convolution.c
@@ -8,16 +8,6 @@
static int dim;
static int saturate;
-static void
-fprintm(int fd, double *m, int dim)
-{
- int i, j;
-
- for(j = 0; j < dim; j++)
- for(i = 0; i < dim; i++)
- fprint(fd, "%g%c", m[j*dim+i], i == dim-1? '\n': '\t');
-}
-
static char *
getline(Biobuf *b)
{
--- a/fns.h
+++ b/fns.h
@@ -8,5 +8,6 @@
Memimage *eallocmemimage(Rectangle, ulong);
Memimage *ereadmemimage(int);
int ewritememimage(int, Memimage*);
+void fprintm(int, double*, int);
void imgbinop(Memimage*, Memimage*, int(*)(uchar, uchar), int);
void imgunaop(Memimage*, int(*)(uchar), int);
--- a/mkfile
+++ b/mkfile
@@ -8,6 +8,7 @@
sobel\
com\
genmask\
+ warp\
OFILES=\
utils.$O\
--- a/utils.c
+++ b/utils.c
@@ -80,6 +80,16 @@
}
void
+fprintm(int fd, double *m, int dim)
+{
+ int i, j;
+
+ for(j = 0; j < dim; j++)
+ for(i = 0; i < dim; i++)
+ fprint(fd, "%g%c", m[j*dim+i], i == dim-1? '\n': '\t');
+}
+
+void
imgbinop(Memimage *i1, Memimage *i2, int(*op)(uchar, uchar), int saturate)
{
uchar *p1, *p1e, *p2;
--- /dev/null
+++ b/warp.c
@@ -1,0 +1,262 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <geometry.h>
+#include "fns.h"
+
+typedef struct Mstk Mstk;
+struct Mstk
+{
+ Matrix3 *items;
+ ulong size;
+};
+
+static double γ; /* roll */
+
+static void
+pushmat(Mstk *stk, Matrix3 m)
+{
+ if(stk->size % 4 == 0)
+ stk->items = erealloc(stk->items, (stk->size + 4)*sizeof(Matrix3));
+ memmove(stk->items[stk->size++], m, sizeof(Matrix3));
+}
+
+static void
+popmat(Mstk *stk, Matrix3 m)
+{
+ memmove(m, stk->items[--stk->size], sizeof(Matrix3));
+ if(stk->size == 0){
+ free(stk->items);
+ stk->items = nil;
+ }
+}
+
+static void
+mkrotation(Matrix3 m, double θ, double φ, double γ)
+{
+ Matrix3 Rx = {
+ 1, 0, 0, 0,
+ 0, cos(θ), -sin(θ), 0,
+ 0, sin(θ), cos(θ), 0,
+ 0, 0, 0, 1,
+ }, Ry = {
+ cos(φ), 0, sin(φ), 0,
+ 0, 1, 0, 0,
+ -sin(φ), 0, cos(φ), 0,
+ 0, 0, 0, 1,
+ }, Rz = {
+ cos(γ), -sin(γ), 0, 0,
+ sin(γ), cos(γ), 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1,
+ };
+ mulm3(Ry, Rz);
+ mulm3(Rx, Ry);
+ memmove(m, Rx, sizeof(Matrix3));
+}
+
+static void
+mkscale(Matrix3 m, double sx, double sy, double sz)
+{
+ sx = sx == 0? 1: 1/sx;
+ sy = sy == 0? 1: 1/sy;
+ sz = sz == 0? 1: 1/sz;
+ Matrix3 S = {
+ sx, 0, 0, 0,
+ 0, sy, 0, 0,
+ 0, 0, sz, 0,
+ 0, 0, 0, 1,
+ };
+ memmove(m, S, sizeof(Matrix3));
+}
+
+static void
+mktranslation(Matrix3 m, double tx, double ty, double tz)
+{
+ Matrix3 T = {
+ 1, 0, 0, tx,
+ 0, 1, 0, ty,
+ 0, 0, 1, tz,
+ 0, 0, 0, 1,
+ };
+ memmove(m, T, sizeof(Matrix3));
+}
+
+static void
+mkshear(Matrix3 m, double shx, double shy, double shz)
+{
+ Matrix3 Sxy = {
+ 1, 0, 0, shx,
+ 0, 1, 0, shy,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1,
+ }, Syz = {
+ 1, 0, 0, 0,
+ shy, 1, 0, 0,
+ shz, 0, 1, 0,
+ 0, 0, 0, 1,
+ }, Sxz = {
+ 1, shx, 0, 0,
+ 0, 1, 0, 0,
+ 0, shz, 1, 0,
+ 0, 0, 0, 1,
+ };
+ mulm3(Syz, Sxy);
+ mulm3(Sxz, Syz);
+ memmove(m, Sxz, sizeof(Matrix3));
+}
+
+static void
+mkxform(Matrix m, Mstk *stk, Memimage *s)
+{
+ double d, focal;
+ int w, h, i, j;
+ Matrix3 U, t;
+
+ w = Dx(s->r);
+ h = Dy(s->r);
+ d = sqrt(w*w + h*h);
+ focal = γ == 0? d: d / 2*sin(γ);
+
+ identity3(U);
+ while(stk->size > 0){
+ popmat(stk, t);
+ mulm3(U, t);
+ }
+
+ Matrix3 T = {
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, focal,
+ 0, 0, 0, 1,
+ }, A1 = {
+ 1, 0, -w/2, 0,
+ 0, 1, -h/2, 0,
+ 0, 0, 1, 0,
+ 0, 0, 1, 0,
+ }, A2 = {
+ focal, 0, w/2, 0,
+ 0, focal, h/2, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 0,
+ };
+
+ mulm3(U, A1);
+ mulm3(T, U);
+ mulm3(A2, T);
+
+ for(j = 0; j < 3; j++)
+ for(i = 0; i < 3; i++)
+ m[j][i] = A2[j][i];
+}
+
+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)
+ return 0; /* edge handler: constant */
+ return *(byteaddr(i, p) + off);
+}
+
+static Memimage *
+imgxform(Memimage *s, Mstk *stk)
+{
+ Memimage *d;
+ Matrix m;
+ Point p, p0;
+ Point2 p2;
+ double Δx, Δy, c[2][2];
+ int i;
+
+ d = eallocmemimage(s->r, s->chan);
+ mkxform(m, stk, s);
+
+ for(p.y = s->r.min.y; p.y < s->r.max.y; p.y++)
+ for(p.x = s->r.min.x; p.x < s->r.max.x; p.x++){
+ p2 = xform((Point2){p.x, p.y, 1}, m);
+ p2 = mulpt2(p2, p2.w == 0? 1: 1/p2.w);
+
+ p0.x = p2.x;
+ p0.y = p2.y;
+ Δx = p2.x - p0.x;
+ Δy = p2.y - p0.y;
+
+ for(i = 0; i < s->nchan; i++){
+ c[0][0] = sample(s, p0, i);
+ c[0][1] = sample(s, addpt(p0, (Point){1,0}), i);
+ c[1][0] = sample(s, addpt(p0, (Point){0,1}), i);
+ c[1][1] = sample(s, addpt(p0, (Point){1,1}), i);
+
+ c[0][0] = flerp(c[0][0], c[0][1], Δx);
+ c[0][1] = flerp(c[1][0], c[1][1], Δx);
+ c[0][0] = flerp(c[0][0], c[0][1], Δy);
+
+ *(byteaddr(d, p) + i) = clamp(c[0][0], 0, 0xFF);
+ }
+ }
+ return d;
+}
+
+static void
+usage(void)
+{
+ fprint(2, "usage: %s [[-s x y z] [-r x y z] [-t x y z] [-S x y z]]...\n", argv0);
+ exits(nil);
+}
+
+void
+main(int argc, char *argv[])
+{
+ Memimage *img, *warp;
+ Mstk stk;
+ Matrix3 m;
+ double x, y, z;
+
+ memset(&stk, 0, sizeof stk);
+ identity3(m);
+ ARGBEGIN{
+ case 's':
+ x = strtod(EARGF(usage()), nil);
+ y = strtod(EARGF(usage()), nil);
+ z = strtod(EARGF(usage()), nil);
+ mkscale(m, x, y, z);
+ pushmat(&stk, m);
+ break;
+ case 'r':
+ x = strtod(EARGF(usage()), nil)*DEG;
+ y = strtod(EARGF(usage()), nil)*DEG;
+ z = strtod(EARGF(usage()), nil)*DEG;
+ γ += z;
+ mkrotation(m, x, y, z);
+ pushmat(&stk, m);
+ break;
+ case 't':
+ x = strtod(EARGF(usage()), nil);
+ y = strtod(EARGF(usage()), nil);
+ z = strtod(EARGF(usage()), nil);
+ mktranslation(m, x, y, z);
+ pushmat(&stk, m);
+ break;
+ case 'S':
+ x = strtod(EARGF(usage()), nil);
+ y = strtod(EARGF(usage()), nil);
+ z = strtod(EARGF(usage()), nil);
+ mkshear(m, x, y, z);
+ pushmat(&stk, m);
+ break;
+ default: usage();
+ }ARGEND;
+ if(argc != 0)
+ usage();
+
+ img = ereadmemimage(0);
+ warp = imgxform(img, &stk);
+ freememimage(img);
+
+ ewritememimage(1, warp);
+ freememimage(warp);
+
+ exits(nil);
+}
--
⑨