From 8328db4e2cb86d06938242cb5a6dc2e2147a93c5 Mon Sep 17 00:00:00 2001 From: Nick Downing Date: Mon, 8 Jan 2024 02:55:36 +1100 Subject: [PATCH] Experimental warping algorithm (command line only for now) --- .gitignore | 4 +++ deps.sh | 2 ++ warp.py | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 .gitignore create mode 100755 deps.sh create mode 100755 warp.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d0f62f6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/a-008_warped.png +/a-008_warped_grid.png +/a-009_warped.png +/a-009_warped_grid.png diff --git a/deps.sh b/deps.sh new file mode 100755 index 0000000..1ee5bbe --- /dev/null +++ b/deps.sh @@ -0,0 +1,2 @@ +#!/bin/sh +pip3 install --user pygobject pycairo numpy opencv-python diff --git a/warp.py b/warp.py new file mode 100755 index 0000000..48fb423 --- /dev/null +++ b/warp.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 + +import cv2 +import imageio +import numpy +import sys + +EXIT_SUCCESS = 0 +EXIT_FAILURE = 1 + +CELL_X = 32 +CELL_Y = 64 + +# find a 3x3 matrix that takes: +# [x00, x01, 1] to [y00, y01, 1] +# [x10, x11, 1] to [y10, y11, 1] +# [x20, x21, 1] to [y20, y11, 1] +# [x30, x31, 1] to [y30, y31, 1] +def perspective_transform(x, y): + A = numpy.zeros((8, 8), numpy.double) + b = numpy.zeros((8,), numpy.double) + A[:4, :2] = x + A[:4, 2] = 1. + A[:4, 6:] = -y[:, :1] * x + b[:4] = y[:, 0] + A[4:, 3:5] = x + A[4:, 5] = 1. + A[4:, 6:] = -y[:, 1:] * x + b[4:] = y[:, 1] + H = numpy.zeros((9,), numpy.double) + H[:8], _, _, _ = numpy.linalg.lstsq(A, b, rcond = None) + H[8] = 1. + H = numpy.reshape(H, (3, 3)) + return H + +if len(sys.argv) < 2: + print(f'usage: {sys.argv[0]:s} file.txt') + sys.exit(EXIT_FAILURE) +file_txt = sys.argv[1] + +in_png = ['a-008.png', 'a-009.png'] +out_png = ['a-008_warped.png', 'a-009_warped.png'] +out_png_grid = ['a-008_warped_grid.png', 'a-009_warped_grid.png'] +with open(file_txt) as fin: + n_pages = int(fin.readline()) + for i in range(n_pages): + state = int(fin.readline()) + nodes = numpy.loadtxt(fin, numpy.double, max_rows = 4) + chars = numpy.loadtxt(fin, numpy.int32, max_rows = 2) + H = numpy.loadtxt(fin, numpy.double, max_rows = 3) + + y = numpy.array( + [ + [-.5, chars[0] + .5, chars[0] + .5, -.5], + [-.5, -.5, chars[1] + .5, chars[1] + .5], + [1., 1., 1., 1.] + ], + numpy.double + ) + x = numpy.linalg.solve(H, y) + x = (x[:2, :] / x[2:, :]).transpose() + + xs = (chars[0] + 1) * CELL_X + ys = (chars[1] + 1) * CELL_Y + y = numpy.array( + [ + [0., 0.], + [xs, 0.], + [xs, ys], + [0., ys] + ], + numpy.double + ) + H = perspective_transform(x, y) + + image = cv2.warpPerspective( + imageio.v2.imread(in_png[i]).astype(numpy.double), + H, + (xs, ys) + ) + image[image < 0.] = 0. + image[image > 1.] = 1. + + imageio.v2.imsave( + out_png[i], + numpy.round(image * 255.).astype(numpy.uint8) + ) + + # convert to RGB with grid + image = numpy.stack([image] * 3, 2) + blue = numpy.array([0., 0., 1.], numpy.double) + for j in range(chars[0] + 1): + image[:, j * CELL_X + CELL_X // 2, :] = blue[numpy.newaxis, :] + for j in range(chars[1] + 1): + image[j * CELL_Y + CELL_Y // 2, :, :] = blue[numpy.newaxis, :] + + imageio.v2.imsave( + out_png_grid[i], + numpy.round(image * 255.).astype(numpy.uint8) + ) -- 2.34.1