簡単な電子透かしを作る
PNG形式の画像はメタデータに情報を載せられる。
How do I save custom information to a PNG Image file in Python?しかし画像の内容に情報を載せる術を考える。
次の方針
- 元の画像の内容について各ピクセルの各色を量子化する
- 漉き込む情報を画像の
ノイズ
として付加する
を元に次の手順
- 元の画像について各ピクセルの各色を5で量子化する
- バイナリーデータの16進表現を取得する
- 各16進表現を2つの2ビット表現に分ける
- 各ピクセルの各色に2ビットのデータを付与する
- 画像を保存する
で情報を画像に漉き込む。
アルファ成分がないRGBの成分で構成される画像を使用する。
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(arr, s):
o = quartetize(s)
if len(o) > len(arr) * len(arr[0]) * len(arr[0][0]): raise ValueError
_arr = arr.copy()
_arr[_arr == 255] -= 1
_arr[:, :, :] = _arr[:, :, :] // 5 * 5
j = 0
for r0 in _arr:
if len(o) == j: break
for r1 in r0:
if len(o) == j: break
for i, c in enumerate(r1):
if len(o) == j: break
r1[i] = c + 1 + o[j]
j += 1
return Image.fromarray(_arr)
def dewatermark(arr, byte=False):
a = []
_arr = arr.copy()
_arr[:, :, :] %= 5
for r0 in _arr:
for r1 in r0:
if r1.any() == 0: break
a.extend(r1)
if r1.any() == 0: break
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')
arr = numpy.array(img)
try: img = watermark(arr, s)
except:
print('The image is too small to contain the data.')
quit()
img.save('data_inserted.png')
img = Image.open('data_inserted.png')
arr = numpy.array(img)
s_decoded = dewatermark(arr)
print('"Quartetized":\n', s_decoded)
print('Is the same?:', s == s_decoded)
画像にとって漉き込まれた情報は量子化で取り除けるノイズだ。
この画像を量子化すればオリジナルの画像を量子化した画像と同じになる。
つまり画像を再利用できるw
2022/03/13 21:14