class Ezstego(BaseCodec):
def __init__(self) -> None:
self.secret: list
super().__init__()
def encode(self, carrier: Image.Image, payload: str) -> list:
im = carrier
im = im.convert("P")
palette = []
im_palette = im.palette.palette
for i in range(int(len(im_palette) / 3)):
palette.append(
(im_palette[3 * i], im_palette[3 * i + 1], im_palette[3 * i + 2])
)
y = []
for i in range(len(palette)):
y.append(
0.299 * palette[i][0] + 0.587 * palette[i][1] + 0.114 * palette[i][2]
)
y_index = np.argsort(y)
y_index_inverse = [0] * 256
for i in range(len(y_index)):
y_index_inverse[y_index[i]] = i
palette_data = im.getpalette()
if palette_data is None:
raise ValueError("Image has no palette")
byte_s = payload.encode("utf-8")
# Convert payload to binary (removes leading '0b') contains string length
hex_bin = byte_s.hex()
oct_bin = oct(int(hex_bin, 16))
binary_bin = bin(int(oct_bin, 8))[2:].zfill(len(hex_bin) * 4)
lenth_bin = bin(len(binary_bin))[2:].zfill(12)
binary_bin = lenth_bin + binary_bin
for i in range(len(binary_bin)):
_y_index = y_index_inverse[palette_data[i]]
lower_bit = _y_index % 2
if lower_bit == int(binary_bin[i]):
continue
elif (lower_bit, binary_bin[i]) == (0, "1"):
palette_data[i] = y_index[_y_index + 1]
elif (lower_bit, binary_bin[i]) == (1, "0"):
palette_data[i] = y_index[_y_index - 1]
self.secret = palette_data
return palette_data
def decode(self, carrier: Image.Image) -> str:
im = carrier
im = im.convert("P")
palette = []
im_palette = im.palette.palette
for i in range(int(len(im_palette) / 3)):
palette.append(
(im_palette[3 * i], im_palette[3 * i + 1], im_palette[3 * i + 2])
)
y = []
for i in range(len(palette)):
y.append(
0.299 * palette[i][0] + 0.587 * palette[i][1] + 0.114 * palette[i][2]
)
y_index = np.argsort(y)
y_index_inverse = [0] * 256
for i in range(len(y_index)):
y_index_inverse[y_index[i]] = i
palette_data = self.secret
info = ""
data_lenth = 14
for i in range(len(palette_data)):
_y_index = y_index_inverse[palette_data[i]]
lower_bit = _y_index % 2
if i >= data_lenth:
break
if lower_bit == 1:
info += "1"
elif lower_bit == 0:
info += "0"
if i == 13:
data_lenth = int(info[:12], 2) + 12
return bytes.fromhex(hex(int(info[12:], 2))[2:]).decode("utf-8")
def getkey(self) -> list:
return self.secret
def setkey(self, key: list) -> None:
self.secret = key