import numpy as np
from PIL import Image
from scipy.fftpack import dct

img = Image.open('/Users/user/puz/README.jpg')
Y = np.asarray(img.convert('YCbCr'))[:, :, 0].astype(float) - 128.0
h, w = Y.shape
bh, bw = h // 8, w // 8

zz = []
for s in range(15):
    for i in range(8):
        j = s - i
        if 0 <= j < 8:
            zz.append((i, j) if s % 2 else (j, i))
idxs = zz[1:9]

bits = np.zeros((bh, bw, 8), dtype=int)
for by in range(bh):
    for bx in range(bw):
        b = Y[by*8:by*8+8, bx*8:bx*8+8]
        c = dct(dct(b.T, norm='ortho').T, norm='ortho')
        for k, (i, j) in enumerate(idxs):
            bits[by, bx, k] = 1 if c[i, j] > 0 else 0

def tobyte(bs):
    v = 0
    for b in bs:
        v = (v << 1) | int(b)
    return v

raw = np.array([[tobyte(bits[y, x]) for x in range(bw)] for y in range(bh)])

print('--- raw grid, first 4 rows (as chars) ---')
for y in range(4):
    print(repr(''.join(chr(c) if 32 <= c < 127 else '.' for c in raw[y])))

print()
print('--- 3x3 majority vote ---')
gh, gw = bh // 3, bw // 3
print('char grid', gw, 'x', gh)
out = []
for gy in range(gh):
    row = []
    for gx in range(gw):
        blk = bits[gy*3:gy*3+3, gx*3:gx*3+3]          # 3x3x8
        maj = (blk.reshape(9, 8).sum(axis=0) >= 5).astype(int)
        row.append(tobyte(maj))
    out.append(bytes(row))

for r in out[:20]:
    print(repr(r))
open('/private/tmp/claude-501/-Users-user-puz/23b7dffc-7a02-481f-ab9c-8220e076a9d4/scratchpad/out.bin', 'wb').write(b''.join(out))
