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

img = Image.open('/Users/user/puz/README.jpg').convert('YCbCr')
Y = np.asarray(img)[:, :, 0].astype(np.float64) - 128.0
H, W = Y.shape
bh, bw = H // 8, W // 8
blocks = Y[:bh*8, :bw*8].reshape(bh, 8, bw, 8).transpose(0, 2, 1, 3)
D = dct(dct(blocks, axis=2, norm='ortho'), axis=3, norm='ortho').reshape(-1, 8, 8)

ZIG = [(0,1),(1,0),(2,0),(1,1),(0,2),(0,3),(1,2),(2,1)]
vals = np.stack([D[:, r, c] for (r, c) in ZIG], axis=1)
bits = (vals > 0).astype(np.uint8)          # 14762 x 8

n = bits.shape[0] // 9
g = bits[:n*9].reshape(n, 9, 8)
maj = (g.sum(axis=1) >= 5).astype(np.uint8)  # majority vote over the 9 replicas
by = np.packbits(maj, axis=1).ravel()

txt = bytes(by).decode('utf-8', errors='replace')
open('decoded.txt', 'w').write(txt)
print(f'{n} bytes recovered')
print(txt[:4000])
