import hashlib
import pathlib as p
import typing as t
import cv2
import numpy as np
Path = t.Union[str, p.Path]
Seed = t.Union[str, int]
class Image:
def __init__(self, data: np.ndarray) -> None:
self._data = data
@classmethod
def from_file(cls, path: Path) -> 'Image':
if p.Path(path).suffix != '.png':
raise NotImplementedError('The bug with non-png images is not yet fixed')
return cls(cv2.imread(str(path)))
@property
def data(self) -> np.ndarray:
return self._data
def encryption(self, seed: Seed) -> 'Image':
rng = self._rng(seed)
height, width, _ = self._data.shape
x = rng.permutation(height)
y = rng.permutation(width)
return self.__class__(self._data[x, :, :][:, y, :])
def decryption(self, seed: Seed) -> 'Image':
rng = self._rng(seed)
height, width, _ = self._data.shape
x = self._inverse_permutation(rng.permutation(height))
y = self._inverse_permutation(rng.permutation(width))
return self.__class__(self._data[x, :, :][:, y, :])
def write(self, path: Path) -> 'Image':
cv2.imwrite(path, self._data)
return self
def _rng(self, seed: Seed) -> np.random.RandomState:
if isinstance(seed, str):
md5 = hashlib.md5(seed.encode('utf-8')).hexdigest()
seed = int(md5[:8], 16)
return np.random.RandomState(seed)
def _inverse_permutation(self, array: np.ndarray) -> np.ndarray:
ans = np.zeros_like(array)
for ith, element in enumerate(array):
ans[element] = ith
return ans
if __name__ == '__main__':
Image \
.from_file('target.png') \
.encryption('Just Monika.') \
.write('output.png')
# Image \
# .from_file('output.png') \
# .decryption('Just Monika.') \
# .write('reverse.png')