簡単な電子透かしを改良する

簡単な電子透かしを作った。
しかしデータを画像の左上から詰めていたので、単一色の画像をベースにするとデータの有無で境界がわかる状態だった。
これはあまり良くないな…と考え、データとして必要な部分を分散させる。
import random
import binascii
from PIL import Image
import numpy

def quartetize(s):
	if type(s) is str: s = s.encode()
	a = []
	f = binascii.b2a_hex(s).decode('ascii')
	for c in f:
		c = int(c, 16)
		a.extend((c>>2, c&0b0011))
	return a

def dequartetize(a, byte=False):
	h = ''
	ps = [a[i:i+2] for i in range(0, len(a), 2)]
	for c in ps: h += hex(c[0]<<2 | c[1])[2:]
	b = binascii.a2b_hex(h.encode('ascii'))
	return b if byte else b.decode()

def watermark(img, s):
	arr = numpy.array(img)
	o = quartetize(s)
	if len(o) > len(arr) * len(arr[0]) * len(arr[0][0]): raise ValueError
	f = [True,] * len(o) + [False,] * ( len(arr) * len(arr[0]) * len(arr[0][0]) - len(o) )
	random.shuffle(f)
	_arr = arr.copy()
	_arr[_arr == 255] -= 1
	_arr[:, :, :] = _arr[:, :, :] // 5 * 5
	j = 0
	k = 0
	for r0 in _arr:
		for r1 in r0:
			for i, c in enumerate(r1):
				if f[k]:
					r1[i] = c + 1 + o[j]
					j += 1
				k += 1
	return Image.fromarray(_arr)

def dewatermark(img, byte=False):
	_arr = numpy.array(img).copy()
	_arr[:, :, :] %= 5
	a = []
	for r0 in _arr:
		for r1 in r0: a.extend(r1)
	d = [x - 1 for x in filter(lambda x: x > 0, a)]
	return dequartetize(d, byte)

s = '''ABCabc123あいう[]'''

print('Original string:\n', s)
print('Length: {} / {} bytes'.format(len(s), len(s.encode())))
print('"Quartetized":\n', quartetize(s))
print('"Dequartetized":\n', dequartetize(quartetize(s)))

img = Image.open('base.png')
try: img = watermark(img, s)
except:
	print('The image is too small to contain the data.')
	quit()
img.save('data_inserted.png')
img = Image.open('data_inserted.png')
s_decoded = dewatermark(img)
print('"Quartetized":\n', s_decoded)
print('Is the same?:', s == s_decoded)
2022/06/07 20:18
タグ