【Python】画像スクランブルを実装する
概要
画像のピクセルを並び替えることで,視覚的に認識ができない画像に変換する画像スクランブルについて調査してみました.
またPythonを用いて画像スクランブル,及び画像の復元まで実装してみたので,こちらにメモを残しておきます.
実装はこちらにて公開しております.
画像スクランブルとは
こちらの論文によると
Image scrambling is the method of rearranging the pixels randomly to make the image visually unreadable and break the correlation between the neighboring pixels.
すなわち,以下の図のように,画像のピクセルを並び替えて,視覚的に理解できないような画像にする技術のことのようです.
この際に,元画像とスクランブル後の画像の二つの間で,並び替え方が不変となるような方法を用いることで,画像の復元も可能です.
今回は画像のRGBの平均画素値のハッシュ値をシードとして並び替えることで,画像の復元を可能にしています.
このスクランブルを画像全体ではなく,画像のパッチごとに実施する,block-wise image scramblingというものもあるようです.(参考論文)
今回実装する画像スクランブルの大まかな流れ
画像内のRGB値の平均値からハッシュ値を計算することで,ピクセルを並び替える前後でハッシュ値が一意になります.
このハッシュ値をシードにしてピクセルの並び替えを行うので,スクランブル画像を元のピクセルの順に並び替えることで画像の復元も可能です.
Pythonで実装する
こちらの実装を参考に実装しました.
参考にした実装ではfor文が使用されていましたが,numpy配列で扱うことで,そのあたりの計算量を減らしています.
import numpy as np from PIL import Image import random from typing import List class ImageScramble(object): """Image Scrambling using Pillow Image. Reference: https://stackoverflow.com/questions/17777760/mixing-pixels-of-an-image-manually-using-python """ @staticmethod def _set_seed_from_image(img: np.ndarray) -> None: """画像からハッシュ値を計算し,シードを設定する. ハッシュ値は,画像スクランブルの前後で変わってはいけない. 今回はRGBそれぞれの画素値の平均からハッシュ値を算出した. Args: img (np.ndarray): input image """ # use RGB mean value to calculate hash value average = np.mean(img.reshape(-1, 3), axis=0) average = tuple(average) random.seed(hash(average)) @staticmethod def _get_pixels(img: np.ndarray) -> np.ndarray: """Get the list of pixel values Args: img (np.ndarray): (h, w, 3) Returns: np.ndarray: (h*w, 3) """ pixels = img.reshape(-1, 3) return pixels @staticmethod def _restore_image( pixels: np.ndarray, original_width: int, original_height: int ) -> np.ndarray: """Restore an image from the list of pixel values Args: pixels (np.ndarray): (h*w, 3) original_width (int): the width of an original image original_height (int): the height of an original image Returns: np.ndarray: (h, w, 3) """ img = pixels.reshape((original_height, original_width, 3)) return img @staticmethod def _scramble_index(pixels: np.ndarray) -> List[int]: """Scramble indicies of pixels. Args: pixels (np.ndarray): (h*w, 3) Returns: List[int]: shuffled index list """ idx = list(range(len(pixels))) random.shuffle(idx) return idx def _scramble_pixels(self, img: np.ndarray) -> np.ndarray: """Scramble pixels Args: img (np.ndarray): (h, w, 3) Returns: np.ndarray: (h*w, 3) """ pixels = self._get_pixels(img) idx = self._scramble_index(pixels) scrambled_pixels = pixels[idx] return scrambled_pixels def scramble(self, img: Image.Image) -> Image.Image: """Do image scrambling. Args: img (Image.Image): input image Returns: Image.Image: scrambled image """ w, h = img.size img = np.asarray(img) self._set_seed_from_image(img) scrambled_pixels = self._scramble_pixels(img) img = self._restore_image(scrambled_pixels, w, h) return Image.fromarray(img) def _unscramble_pixels(self, img: np.ndarray) -> np.ndarray: """Unscramble pixels Args: img (np.ndarray): (h, w, 3) Returns: np.ndarray: (h*w, 3) """ pixels = self._get_pixels(img) idx = self._scramble_index(pixels) # argsortを使って元画像のピクセルのインデックスを取得する idx = np.argsort(idx) original_pixels = pixels[idx] return original_pixels def unscramble(self, img: Image.Image) -> Image.Image: """Do unscramgling image Args: img (Image.Image): input scrambled image. Returns: Image.Image: unscrambled image. """ w, h = img.size img = np.asarray(img) self._set_seed_from_image(img) original_pixels = self._unscramble_pixels(img) img = self._restore_image(original_pixels, w, h) return Image.fromarray(img)
実際に使ってみた結果
こちらの画像を使用して画像スクランブルを実装してみます.
img = Image.open("./dog.jpeg") processor = ImageScramble() scrambled = processor.scramble(img) scrambled.save("scramble.png") unscrambled = processor.unscramble(scrambled) unscrambled.save("unscramble.png")
画像スクランブルを実行した結果(scramble.png
)が以下です.
しっかりとピクセルの順番が代わり,視覚的に認識できない画像になりました.
続いて,スクランブル画像から元の画像を復元した結果(unscramble.png
)が以下です.
しっかりと復元できていました!
まとめ
画像のピクセルの順番を並び替えて,視覚的に認識できないようにする画像スクランブルを実装してみました.
スクランブルされた画像に対して画像認識を試みる研究もあるようなので,今後試してみたいと思います.
参考
- mixing pixels of an image manually using python - Stack Overflow
- (PDF) Cryptographic image Scrambling techniques
- 株式会社 エス・イー・シー 画像スクランブルの試用ページ
- [2001.07761] Block-wise Scrambled Image Recognition Using Adaptation Network