--- /dev/null
+__pycache__
+[a-z]*.jpg
--- /dev/null
+#!/usr/bin/env python3
+
+import gamma
+import numpy
+import perspective
+import scipy
+import scipy.ndimage
+import sys
+
+sys.stderr.write(
+ '''stdin: control blocks description
+xs0,ys0 xs1,ys1 xs2,ys2 xs3,ys3 : size (pixels)
+xf0,yf0 xf1,yf1 xf2,yf2 xf3,yf3 : freedom (fraction of size)
+in.jpg out.jpg x0,y0 x1,y1 x2,y2 x3,y3 : top left (pixels)
+...
+'''
+)
+
+size = numpy.array(
+ [
+ [int(j) for j in i.split(',')]
+ for i in sys.stdin.readline().split()
+ ],
+ numpy.int32
+).transpose()
+assert size.shape == (2, 4)
+freedom = numpy.array(
+ [
+ [float(j) for j in i.split(',')]
+ for i in sys.stdin.readline().split()
+ ],
+ numpy.double
+).transpose()
+assert freedom.shape == (2, 4)
+
+files = []
+line = sys.stdin.readline()
+while len(line):
+ fields = line.split()
+ in_jpg = fields[0]
+ out_jpg = fields[1]
+ top_left = numpy.array(
+ [
+ [int(j) for j in i.split(',')]
+ for i in fields[2:]
+ ],
+ numpy.int32
+ ).transpose()
+ assert top_left.shape == (2, 4)
+ files.append((in_jpg, out_jpg, top_left))
+ line = sys.stdin.readline()
+
+in_jpg, out_jpg, top_left0 = files[0]
+sys.stderr.write(f'read {in_jpg:s}\n')
+image0 = gamma.read_image(in_jpg)
+shape = image0.shape
+sys.stderr.write(f'shape {str(shape):s}\n')
+
+sys.stderr.write(f'write {out_jpg:s}\n')
+gamma.write_image(out_jpg, image0)
+
+A = numpy.identity(3, numpy.double)
+for i in range(len(files) - 1):
+ in_jpg, out_jpg, top_left1 = files[i + 1]
+ sys.stderr.write(f'read {in_jpg:s}\n')
+ image1 = gamma.read_image(in_jpg)
+ assert image1.shape == shape
+
+ p = []
+ q = []
+ for j in range(4):
+ xs, ys = size[:, j]
+ xf, yf = freedom[:, j]
+ xf = int(round(xf * xs))
+ yf = int(round(yf * ys))
+ x0, y0 = top_left0[:, j]
+ x1, y1 = top_left1[:, j]
+
+ block0 = image0[
+ max(y0, 0):max(y0 + ys, 0),
+ max(x0, 0):max(x0 + xs, 0),
+ :
+ ]
+ #gamma.write_image(f'diag{j:d}.jpg', block0)
+ mean0 = numpy.mean(numpy.mean(block0, 0), 0)
+ data0 = block0 - mean0[numpy.newaxis, numpy.newaxis, :]
+
+ best = None
+ for x in range(-xf, xf + 1):
+ for y in range(-yf, yf + 1):
+ block1 = image1[
+ max(y1 + y, 0):max(y1 + y + ys, 0),
+ max(x1 + x, 0):max(x1 + x + xs, 0),
+ :
+ ]
+ if block1.shape != block0.shape:
+ continue
+ mean1 = numpy.mean(numpy.mean(block1, 0), 0)
+ data1 = block1 - mean1[numpy.newaxis, numpy.newaxis, :]
+
+ score = numpy.sum(data0 * data1)
+ if best is None or score > best[0]:
+ best = (score, x, y)
+
+ score, x, y = best
+ sys.stderr.write(f'score {score:.3f} x {x / xs:.3f} y {y / ys:.3f}\n')
+ p.append(numpy.array([x0 + xs / 2, y0 + ys / 2], numpy.double))
+ q.append(numpy.array([x1 + x + xs / 2, y1 + y + ys / 2], numpy.double))
+
+ top_left1[:, j] = (x1 + x, y1 + y)
+ p = numpy.stack(p, 1)
+ q = numpy.stack(q, 1)
+ A = perspective.calc_transform(p, q) @ A # makes the transform cumulative
+
+ sys.stderr.write('remap\n')
+ ys, xs, cs = shape
+ #def mapping(p):
+ # #return tuple(
+ # # perspective.apply_transform(A, numpy.array(p, numpy.double)[::-1])[::-1]
+ # #)
+ # q = A[:, 0] * p[1] + A[:, 1] * p[0] + A[:, 2]
+ # return (q[1] / q[2], q[0] / q[2])
+ #out_image1 = numpy.stack(
+ # [
+ # scipy.ndimage.geometric_transform(image1[:, :, j], mapping)
+ # for j in range(cs)
+ # ],
+ # 2
+ #)
+ coords = numpy.zeros((2, ys, xs), numpy.double)
+ coords[0, :, :] = numpy.arange(ys, dtype = numpy.double)[:, numpy.newaxis]
+ coords[1, :, :] = numpy.arange(xs, dtype = numpy.double)[numpy.newaxis, :]
+ mapped_coords = perspective.apply_transform_multi(
+ A,
+ coords[::-1, :, :].reshape((2, ys * xs))
+ ).reshape(2, ys, xs)[::-1, :, :]
+ out_image1 = numpy.stack(
+ [
+ scipy.ndimage.map_coordinates(image1[:, :, j], mapped_coords)
+ for j in range(cs)
+ ],
+ 2
+ )
+
+ sys.stderr.write(f'write {out_jpg:s}\n')
+ gamma.write_image(out_jpg, out_image1)
+
+ image0 = image1
+ top_left0 = top_left1
+
+sys.stdout.write(
+ ' '.join(
+ [
+ ','.join([str(size[j, i]) for j in range(2)])
+ for i in range(4)
+ ]
+ ) + '\n'
+)
+sys.stdout.write(
+ ' '.join(
+ [
+ ','.join([str(freedom[j, i]) for j in range(2)])
+ for i in range(4)
+ ]
+ ) + '\n'
+)
+for in_jpg, out_jpg, top_left in files:
+ sys.stdout.write(
+ ' '.join(
+ [in_jpg, out_jpg] +
+ [
+ ','.join([str(top_left[j, i]) for j in range(2)])
+ for i in range(4)
+ ]
+ ) + '\n'
+ )
--- /dev/null
+#!/usr/bin/env python3
+
+import gamma
+import numpy
+import perspective
+import scipy
+import scipy.ndimage
+import sys
+
+sys.stderr.write(
+ '''stdin: control blocks description
+xs0,ys0 xs1,ys1 xs2,ys2 xs3,ys3 : size (pixels)
+xf0,yf0 xf1,yf1 xf2,yf2 xf3,yf3 : freedom (fraction of size)
+in.jpg out.jpg x0,y0 x1,y1 x2,y2 x3,y3 : top left (pixels)
+...
+'''
+)
+
+size = numpy.array(
+ [
+ [int(j) for j in i.split(',')]
+ for i in sys.stdin.readline().split()
+ ],
+ numpy.int32
+).transpose()
+assert size.shape == (2, 4)
+freedom = numpy.array(
+ [
+ [float(j) for j in i.split(',')]
+ for i in sys.stdin.readline().split()
+ ],
+ numpy.double
+).transpose()
+assert freedom.shape == (2, 4)
+
+files = []
+line = sys.stdin.readline()
+while len(line):
+ fields = line.split()
+ in_jpg = fields[0]
+ out_jpg = fields[1]
+ top_left = numpy.array(
+ [
+ [int(j) for j in i.split(',')]
+ for i in fields[2:]
+ ],
+ numpy.int32
+ ).transpose()
+ assert top_left.shape == (2, 4)
+ files.append((in_jpg, out_jpg, top_left))
+ line = sys.stdin.readline()
+
+sys.stdout.write(
+ ' '.join(
+ [
+ ','.join([str(size[j, i] // 2) for j in range(2)])
+ for i in range(4)
+ ]
+ ) + '\n'
+)
+sys.stdout.write(
+ ' '.join(
+ [
+ ','.join([str(freedom[j, i]) for j in range(2)])
+ for i in range(4)
+ ]
+ ) + '\n'
+)
+for in_jpg, out_jpg, top_left in files:
+ sys.stdout.write(
+ ' '.join(
+ [in_jpg, out_jpg] +
+ [
+ ','.join([str(top_left[j, i] // 2) for j in range(2)])
+ for i in range(4)
+ ]
+ ) + '\n'
+ )
--- /dev/null
+#!/usr/bin/env python3
+
+import imageio
+import numpy
+
+def read_image(in_file):
+ # read double image
+ image = imageio.imread(in_file) / 255.
+
+ # gamma decode
+ linear = image < .0031308 * 12.92
+ image[linear] /= 12.92
+ image[~linear] = ((image[~linear] + .055) / 1.055) ** 2.4
+
+ return image
+
+def write_image(out_file, image):
+ image = numpy.copy(image)
+
+ # gamma encode
+ linear = image < .0031308
+ image[linear] *= 12.92
+ image[~linear] = 1.055 * image[~linear] ** (1. / 2.4) - .055
+
+ # write double image
+ image[image < 0.] = 0.
+ image[image > 1.] = 1.
+ imageio.imwrite(
+ out_file,
+ numpy.round(image * 255.).astype(numpy.uint8)
+ )
+
+if __name__ == '__main__':
+ import sys
+
+ if len(sys.argv) < 3:
+ print(f'usage: {sys.argv[0]:s} in_file out_file')
+ sys.exit(1)
+ in_file = sys.argv[1]
+ out_file = sys.argv[2]
+
+ write_image(out_file, read_image(in_file))
--- /dev/null
+184,156 184,156 184,156 184,156
+.15,.15 .15,.15 .3,.3 .3,.3
+IMG_2336.jpg out0_2336.jpg 396,268 3964,420 628,1880 3856,1828
+IMG_2337.jpg out0_2337.jpg 360,268 3932,396 612,1888 3824,1796
+IMG_2338.jpg out0_2338.jpg 436,220 3996,368 668,1828 3888,1764
+IMG_2339.jpg out0_2339.jpg 412,212 3968,352 656,1824 3868,1736
+IMG_2340.jpg out0_2340.jpg 328,326 3900,456 584,1948 3792,1860
+IMG_2341.jpg out0_2341.jpg 392,288 3952,456 624,1916 3836,1860
+IMG_2342.jpg out0_2342.jpg 368,252 3952,440 620,1884 3836,1832
+IMG_2343.jpg out0_2343.jpg 396,304 3952,476 628,1928 3832,1876
+IMG_2344.jpg out0_2344.jpg 356,304 3920,444 604,1920 3812,1844
+IMG_2345.jpg out0_2345.jpg 392,296 3952,456 636,1912 3844,1844
+IMG_2346.jpg out0_2346.jpg 380,268 3956,424 636,1892 3844,1812
+IMG_2347.jpg out0_2347.jpg 380,304 3932,464 612,1924 3836,1852
+IMG_2348.jpg out0_2348.jpg 416,264 3976,428 648,1876 3864,1820
+IMG_2349.jpg out0_2349.jpg 404,292 3968,468 644,1908 3864,1856
+IMG_2350.jpg out0_2350.jpg 372,288 3936,424 628,1908 3844,1816
+IMG_2351.jpg out0_2351.jpg 416,272 3992,420 664,1888 3884,1816
+IMG_2352.jpg out0_2352.jpg 372,248 3948,420 624,1880 3848,1816
+IMG_2353.jpg out0_2353.jpg 428,300 3992,492 660,1924 3892,1876
+IMG_2355.jpg out0_2355.jpg 384,244 3956,396 636,1872 3852,1796
+IMG_2356.jpg out0_2356.jpg 372,256 3940,388 624,1872 3824,1796
+IMG_2357.jpg out0_2357.jpg 328,276 3920,388 588,1880 3824,1796
+IMG_2358.jpg out0_2358.jpg 404,240 3980,412 648,1856 3872,1820
+IMG_2359.jpg out0_2359.jpg 380,272 3956,436 628,1876 3852,1832
+IMG_2360.jpg out0_2360.jpg 372,292 3940,448 624,1908 3852,1832
+IMG_2361.jpg out0_2361.jpg 376,256 3940,408 624,1880 3852,1792
+IMG_2362.jpg out0_2362.jpg 320,320 3892,436 580,1928 3792,1836
--- /dev/null
+184,156 184,156 184,156 184,156
+0.15,0.15 0.15,0.15 0.3,0.3 0.3,0.3
+IMG_2336.jpg out0_2336.jpg 396,268 3964,420 628,1880 3856,1828
+IMG_2337.jpg out0_2337.jpg 357,279 3924,391 609,1884 3835,1798
+IMG_2338.jpg out0_2338.jpg 418,218 3988,369 666,1818 3896,1772
+IMG_2339.jpg out0_2339.jpg 394,217 3964,340 651,1813 3882,1744
+IMG_2340.jpg out0_2340.jpg 326,339 3882,446 587,1933 3809,1847
+IMG_2341.jpg out0_2341.jpg 385,303 3940,453 639,1894 3863,1850
+IMG_2342.jpg out0_2342.jpg 383,264 3942,432 637,1852 3866,1827
+IMG_2343.jpg out0_2343.jpg 377,325 3931,479 635,1915 3865,1876
+IMG_2344.jpg out0_2344.jpg 346,325 3902,444 614,1911 3846,1845
+IMG_2345.jpg out0_2345.jpg 387,310 3941,458 647,1895 3883,1858
+IMG_2346.jpg out0_2346.jpg 384,284 3941,421 649,1868 3888,1821
+IMG_2347.jpg out0_2347.jpg 370,325 3923,465 635,1909 3872,1868
+IMG_2348.jpg out0_2348.jpg 406,270 3964,424 668,1855 3913,1828
+IMG_2349.jpg out0_2349.jpg 402,311 3956,475 661,1896 3905,1879
+IMG_2350.jpg out0_2350.jpg 363,300 3921,418 633,1883 3882,1825
+IMG_2351.jpg out0_2351.jpg 417,289 3976,409 687,1871 3939,1819
+IMG_2352.jpg out0_2352.jpg 379,271 3936,413 644,1856 3896,1821
+IMG_2353.jpg out0_2353.jpg 425,320 3978,484 686,1907 3939,1895
+IMG_2355.jpg out0_2355.jpg 383,261 3941,398 653,1843 3907,1807
+IMG_2356.jpg out0_2356.jpg 372,269 3929,393 644,1852 3879,1808
+IMG_2357.jpg out0_2357.jpg 340,287 3896,386 617,1868 3854,1799
+IMG_2358.jpg out0_2358.jpg 409,253 3964,414 677,1834 3916,1823
+IMG_2359.jpg out0_2359.jpg 386,282 3938,432 656,1865 3896,1842
+IMG_2360.jpg out0_2360.jpg 379,301 3930,447 653,1886 3894,1857
+IMG_2361.jpg out0_2361.jpg 368,266 3933,405 656,1852 3904,1815
+IMG_2362.jpg out0_2362.jpg 308,326 3869,437 602,1914 3847,1847
--- /dev/null
+184,156 184,156 184,156 184,156
+.15,.15 .15,.15 .3,.3 .3,.3
+out0_2336.jpg out1_2336.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2337.jpg out1_2337.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2338.jpg out1_2338.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2339.jpg out1_2339.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2340.jpg out1_2340.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2341.jpg out1_2341.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2342.jpg out1_2342.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2343.jpg out1_2343.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2344.jpg out1_2344.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2345.jpg out1_2345.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2346.jpg out1_2346.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2347.jpg out1_2347.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2348.jpg out1_2348.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2349.jpg out1_2349.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2350.jpg out1_2350.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2351.jpg out1_2351.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2352.jpg out1_2352.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2353.jpg out1_2353.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2355.jpg out1_2355.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2356.jpg out1_2356.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2357.jpg out1_2357.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2358.jpg out1_2358.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2359.jpg out1_2359.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2360.jpg out1_2360.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2361.jpg out1_2361.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2362.jpg out1_2362.jpg 396,268 3964,420 628,1880 3856,1828
--- /dev/null
+184,156 184,156 184,156 184,156
+0.15,0.15 0.15,0.15 0.3,0.3 0.3,0.3
+out0_2336.jpg out1_2336.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2337.jpg out1_2337.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2338.jpg out1_2338.jpg 396,268 3964,420 628,1880 3856,1828
+out0_2339.jpg out1_2339.jpg 397,268 3965,420 629,1880 3856,1828
+out0_2340.jpg out1_2340.jpg 396,267 3965,420 629,1880 3856,1829
+out0_2341.jpg out1_2341.jpg 396,266 3965,420 629,1880 3856,1829
+out0_2342.jpg out1_2342.jpg 396,266 3965,420 629,1880 3856,1828
+out0_2343.jpg out1_2343.jpg 396,266 3966,420 630,1880 3856,1828
+out0_2344.jpg out1_2344.jpg 396,266 3967,421 631,1880 3856,1828
+out0_2345.jpg out1_2345.jpg 396,266 3967,421 631,1880 3855,1828
+out0_2346.jpg out1_2346.jpg 397,266 3967,421 631,1880 3855,1828
+out0_2347.jpg out1_2347.jpg 397,265 3967,422 631,1880 3855,1828
+out0_2348.jpg out1_2348.jpg 397,265 3967,422 631,1880 3855,1828
+out0_2349.jpg out1_2349.jpg 397,265 3967,422 631,1880 3855,1828
+out0_2350.jpg out1_2350.jpg 397,265 3967,422 631,1880 3855,1828
+out0_2351.jpg out1_2351.jpg 397,265 3967,422 631,1880 3856,1827
+out0_2352.jpg out1_2352.jpg 397,268 3967,422 631,1880 3856,1827
+out0_2353.jpg out1_2353.jpg 397,268 3967,422 631,1881 3856,1827
+out0_2355.jpg out1_2355.jpg 397,268 3969,422 631,1881 3859,1827
+out0_2356.jpg out1_2356.jpg 397,268 3969,422 631,1881 3882,1823
+out0_2357.jpg out1_2357.jpg 397,267 3969,422 631,1881 3882,1825
+out0_2358.jpg out1_2358.jpg 397,267 3969,422 631,1881 3882,1826
+out0_2359.jpg out1_2359.jpg 397,267 3969,422 631,1881 3883,1827
+out0_2360.jpg out1_2360.jpg 397,267 3969,422 631,1881 3883,1828
+out0_2361.jpg out1_2361.jpg 400,266 3969,422 631,1881 3883,1829
+out0_2362.jpg out1_2362.jpg 400,266 3969,422 631,1881 3884,1829
--- /dev/null
+#!/bin/sh
+for i in out1_*.jpg
+do
+ j=`echo $i |sed -e 's/out1/down/'`
+ convert $i -resize 2145x1428 $j
+done
--- /dev/null
+#!/bin/sh
+ffmpeg -framerate 5 -pattern_type glob -i 'post_23*.jpg' -s 1366x768 -pix_fmt yuv420p -c:v libx264 post.mp4
--- /dev/null
+44,94
+2038,1146
+down_2336.jpg post_2336.jpg
+down_2337.jpg post_2337.jpg
+down_2338.jpg post_2338.jpg
+down_2339.jpg post_2339.jpg
+down_2340.jpg post_2340.jpg
+down_2341.jpg post_2341.jpg
+down_2342.jpg post_2342.jpg
+down_2343.jpg post_2343.jpg
+down_2344.jpg post_2344.jpg
+down_2345.jpg post_2345.jpg
+down_2346.jpg post_2346.jpg
+down_2347.jpg post_2347.jpg
+down_2348.jpg post_2348.jpg
+down_2349.jpg post_2349.jpg
+down_2350.jpg post_2350.jpg
+down_2351.jpg post_2351.jpg
+down_2352.jpg post_2352.jpg
+down_2353.jpg post_2353.jpg
+down_2355.jpg post_2355.jpg
+down_2356.jpg post_2356.jpg
+down_2357.jpg post_2357.jpg
+down_2358.jpg post_2358.jpg
+down_2359.jpg post_2359.jpg
+down_2360.jpg post_2360.jpg
+down_2361.jpg post_2361.jpg
+down_2362.jpg post_2362.jpg
--- /dev/null
+#!/usr/bin/env python3
+
+import gamma
+import numpy
+import sys
+
+if len(sys.argv) < 3:
+ print(f'usage: {sys.argv[0]:s} in1.jpg ... inN.jpg out.jpg')
+ sys.exit(1)
+in_jpgs = sys.argv[1:-1]
+out_jpg = sys.argv[-1]
+
+images = []
+for in_jpg in in_jpgs:
+ print('read', in_jpg)
+ images.append(gamma.read_image(in_jpg))
+
+image = numpy.mean(numpy.stack(images, 0), 0)
+print('write', out_jpg)
+gamma.write_image(out_jpg, image)
--- /dev/null
+#!/usr/bin/env python3
+
+import numpy
+import numpy.linalg
+
+# call with 2x4 vector (4 points of x, y)
+def calc_basis(p):
+ # see https://math.stackexchange.com/questions/296794/finding-the-transform-matrix-from-4-projected-points-with-javascript
+ A = numpy.ones((3, 4), numpy.double)
+ A[:2, :] = p
+ return A[:, :3] * numpy.linalg.solve(A[:, :3], A[:, 3])[numpy.newaxis, :]
+
+def calc_transform(p, q):
+ A = calc_basis(p)
+ #y = A @ numpy.array(
+ # [
+ # [1., 0., 0., 1.],
+ # [0., 1., 0., 1.],
+ # [0., 0., 1., 1.]
+ # ],
+ # numpy.double
+ #)
+ #print ('xxx', y[:2, :] / y[2:, :], p)
+
+ B = calc_basis(q)
+ #y = B @ numpy.array(
+ # [
+ # [1., 0., 0., 1.],
+ # [0., 1., 0., 1.],
+ # [0., 0., 1., 1.]
+ # ],
+ # numpy.double
+ #)
+ #print ('yyy', y[:2, :] / y[2:, :], q)
+
+ #return B @ numpy.linalg.inv(A)
+ return numpy.linalg.solve(A.transpose(), B.transpose()).transpose()
+
+# call with 2xN vector (N points of x, y), computes 2xN vector
+def apply_transform_multi(A, p):
+ x = numpy.ones((3, p.shape[1]), numpy.double)
+ x[:2, :] = p
+ y = A @ x
+ return y[:2, :] / y[2:, :]
+
+# call with 2-vector (x, y), computes 2-vector
+def apply_transform(A, p):
+ return apply_transform_multi(A, p[:, numpy.newaxis])[:, 0]
+
+if __name__ == '__main__':
+ x = numpy.array([[11., 52., 23., 74.], [15., 36., 27., 58.]], numpy.double)
+ y = numpy.array([[31., 92., 73., 24.], [65., 26., 87., 18.]], numpy.double)
+ A = calc_transform(x, y)
+ print(apply_transform_multi(A, x), y)
--- /dev/null
+#!/usr/bin/env python3
+
+import gamma
+import numpy
+import perspective
+import scipy
+import scipy.ndimage
+import sys
+
+sys.stderr.write(
+ '''stdin: crop and file list
+x0,y0 : top left (pixels)
+xs,ys : size (pixels)
+in.jpg out.jpg
+...
+'''
+)
+
+x0, y0 = (int(i) for i in sys.stdin.readline().split(','))
+xs, ys = (int(i) for i in sys.stdin.readline().split(','))
+
+images = []
+out_jpgs = []
+line = sys.stdin.readline()
+while len(line):
+ fields = line.split()
+ in_jpg = fields[0]
+ out_jpg = fields[1]
+ sys.stderr.write(f'read {in_jpg:s}\n')
+ images.append(gamma.read_image(in_jpg)[y0:y0 + ys, x0:x0 + xs])
+ out_jpgs.append(out_jpg)
+ line = sys.stdin.readline()
+images = numpy.stack(images, 0)
+
+# get standardization parameters and remove from images
+#mean = numpy.mean(numpy.mean(images, 1), 1)
+#images -= mean[:, numpy.newaxis, numpy.newaxis, :]
+stddev = numpy.sqrt(numpy.mean(numpy.mean(numpy.square(images), 1), 1))
+images /= stddev[:, numpy.newaxis, numpy.newaxis, :]
+
+# find a middle ground of standardization parameters and apply to images
+stddev = numpy.mean(stddev, 0)
+images *= stddev[numpy.newaxis, numpy.newaxis, numpy.newaxis, :]
+#mean = numpy.mean(mean, 0)
+#images += mean[numpy.newaxis, numpy.newaxis, numpy.newaxis, :]
+
+for i in range(len(out_jpgs)):
+ sys.stderr.write(f'write {out_jpgs[i]:s}\n')
+ gamma.write_image(out_jpgs[i], images[i, :, :, :])
--- /dev/null
+stdin: control blocks description
+xs0,ys0 xs1,ys1 xs2,ys2 xs3,ys3 : size (pixels)
+xf0,yf0 xf1,yf1 xf2,yf2 xf3,yf3 : freedom (fraction of size)
+in.png out.png x0,y0 x1,y1 x2,y2 x3,y3 : top left (pixels)
+...
+read IMG_2327.jpg
+shape (2856, 4290, 3)
+write out_2327.jpg
+read IMG_2328.jpg
+score 715.6004478076007 x -0.017857142857142856 y 0.08108108108108109
+score 1308.730653335997 x -0.012048192771084338 y -0.007246376811594203
+score 3261.3368802529485 x -0.006024096385542169 y -0.06159420289855073
+score 2253.837711191594 x 0.012048192771084338 y -0.057971014492753624
+remap
+write out_2328.jpg
+read IMG_2329.jpg
+score 1134.7831522804056 x -0.010714285714285714 y -0.02702702702702703
+score 1360.293699494943 x -0.012048192771084338 y 0.04710144927536232
+score 3446.662509676817 x 0.008032128514056224 y -0.0036231884057971015
+score 2328.027725920055 x -0.04417670682730924 y 0.03260869565217391
+remap
+write out_2329.jpg
+read IMG_2330.jpg
+score 1411.0848982232158 x -0.02142857142857143 y 0.006756756756756757
+score 1379.6615796156616 x 0.006024096385542169 y -0.07608695652173914
+score 3251.43782216589 x 0.02208835341365462 y 0.025362318840579712
+score 2157.285766963175 x 0.0321285140562249 y -0.021739130434782608
+remap
+write out_2330.jpg
+read IMG_2331.jpg
+score 133.93963796314597 x -0.039285714285714285 y 0.10135135135135136
+score 1356.2276467853533 x -0.018072289156626505 y 0.010869565217391304
+score 3019.2715869949907 x 0.008032128514056224 y -0.014492753623188406
+score 1859.1247388153474 x -0.030120481927710843 y 0.025362318840579712
+remap
+write out_2331.jpg
+read IMG_2332.jpg
+score 205.53166328958875 x 0.02142857142857143 y -0.02027027027027027
+score 1415.7396923487422 x 0.012048192771084338 y 0.07246376811594203
+score 3661.7427680812484 x -0.05220883534136546 y -0.014492753623188406
+score 2317.4793590874656 x 0.014056224899598393 y -0.03260869565217391
+remap
+write out_2332.jpg
+read IMG_2333.jpg
+score 290.4830488338663 x -0.0035714285714285713 y 0.060810810810810814
+score 1772.6169397367855 x -0.006024096385542169 y 0.014492753623188406
+score 4627.372826905764 x 0.0321285140562249 y 0.03260869565217391
+score 3258.725320051333 x -0.004016064257028112 y -0.036231884057971016
+remap
+write out_2333.jpg
+read IMG_2334.jpg
+score 304.95577362805903 x -0.04285714285714286 y 0.0
+score 1883.1239174497493 x 0.004016064257028112 y -0.06521739130434782
+score 4962.63761528914 x -0.05421686746987952 y -0.0036231884057971015
+score 3246.0699811792706 x -0.008032128514056224 y 0.043478260869565216
+remap
+write out_2334.jpg
--- /dev/null
+280,148 498,276 498,276 498,276
+.1,.1 .1,.1 .1,.1 .1,.1
+IMG_2327.jpg out0_2327.jpg 246,692 3765,735 408,2478 3657,2541
+IMG_2328.jpg out0_2328.jpg 52,660 3576,687 186,2478 3438,2502
+IMG_2329.jpg out0_2329.jpg 174,620 3711,663 309,2430 3588,2481
+IMG_2330.jpg out0_2330.jpg 36,722 3558,753 144,2529 3414,2553
+IMG_2331.jpg out0_2331.jpg 90,726 3624,771 198,2523 3483,2565
+IMG_2332.jpg out0_2332.jpg 80,598 3621,594 234,2394 3492,2412
+IMG_2333.jpg out0_2333.jpg 30,604 3573,555 171,2400 3444,2391
+IMG_2334.jpg out0_2334.jpg 166,510 3699,525 315,2298 3570,2322
--- /dev/null
+280,148 498,276 498,276 498,276
+.1,.1 .1,.1 .1,.1 .1,.1
+IMG_2327.jpg out0_2327.jpg 246,692 3765,735 408,2478 3657,2541
+IMG_2328.jpg out0_2328.jpg 47,672 3570,685 183,2461 3444,2486
+IMG_2329.jpg out0_2329.jpg 167,627 3699,674 309,2413 3573,2474
+IMG_2330.jpg out0_2330.jpg 23,730 3549,743 154,2520 3414,2540
+IMG_2331.jpg out0_2331.jpg 82,722 3606,765 212,2511 3468,2559
+IMG_2332.jpg out0_2332.jpg 77,592 3610,608 222,2378 3484,2397
+IMG_2333.jpg out0_2333.jpg 26,607 3560,573 175,2393 3434,2366
+IMG_2334.jpg out0_2334.jpg 150,513 3688,525 292,2290 3555,2309
--- /dev/null
+280,148 498,276 498,276 498,276
+.025,.025 .025,.025 .025,.025 .025,.025
+out0_2327.jpg out1_2327.jpg 246,692 3765,735 408,2478 3657,2541
+out0_2328.jpg out1_2328.jpg 246,692 3765,735 408,2478 3657,2541
+out0_2329.jpg out1_2329.jpg 246,692 3765,735 408,2478 3657,2541
+out0_2330.jpg out1_2330.jpg 246,692 3765,735 408,2478 3657,2541
+out0_2331.jpg out1_2331.jpg 246,692 3765,735 408,2478 3657,2541
+out0_2332.jpg out1_2332.jpg 246,692 3765,735 408,2478 3657,2541
+out0_2333.jpg out1_2333.jpg 246,692 3765,735 408,2478 3657,2541
+out0_2334.jpg out1_2334.jpg 246,692 3765,735 408,2478 3657,2541
--- /dev/null
+280,148 498,276 498,276 498,276
+0.025,0.025 0.025,0.025 0.025,0.025 0.025,0.025
+out0_2327.jpg out1_2327.jpg 246,692 3765,735 408,2478 3657,2541
+out0_2328.jpg out1_2328.jpg 247,692 3765,735 408,2478 3657,2541
+out0_2329.jpg out1_2329.jpg 246,692 3765,735 408,2478 3657,2542
+out0_2330.jpg out1_2330.jpg 245,693 3765,735 408,2478 3657,2543
+out0_2331.jpg out1_2331.jpg 244,695 3765,735 408,2478 3657,2543
+out0_2332.jpg out1_2332.jpg 243,695 3765,736 408,2478 3657,2543
+out0_2333.jpg out1_2333.jpg 243,695 3765,736 408,2478 3657,2543
+out0_2334.jpg out1_2334.jpg 243,695 3765,736 407,2479 3657,2543
--- /dev/null
+#!/bin/sh
+#ffmpeg -framerate 5 -pattern_type glob -i 'out0_23*.jpg' -filter:v "crop=4020:2260:272:224" -s 1366x768 -pix_fmt yuv420p -c:v libx264 out0.mp4
+#ffmpeg -framerate 5 -pattern_type glob -i 'out1_23*.jpg' -filter:v "crop=4020:2260:272:224" -s 1366x768 -pix_fmt yuv420p -c:v libx264 out1.mp4
+ffmpeg -framerate 5 -pattern_type glob -i 'post_23*.jpg' -s 1366x768 -pix_fmt yuv420p -c:v libx264 post.mp4
--- /dev/null
+272,224
+4020,2260
+out1_2327.jpg post_2327.jpg
+out1_2328.jpg post_2328.jpg
+out1_2329.jpg post_2329.jpg
+out1_2330.jpg post_2330.jpg
+out1_2331.jpg post_2331.jpg
+out1_2332.jpg post_2332.jpg
+out1_2333.jpg post_2333.jpg
+out1_2334.jpg post_2334.jpg