本当なら Mathematica ver.8 の話を書くべきなのだろうが、僕はまだいろいろ分かるほどは触っていない。 ver.8 の話は、日本語版の販売が開始されてから腰を落ち着けて書くことにして、今日は、紹介しようと思ってのびのびになっていた、半年くらい前の Wolfram Blog の記事を紹介する。元記事のアドレスは下記。

http://blog.wolfram.com/2010/07/08/doing-spy-stuff-with-mathematica/

この記事で紹介されている内容は本稿のタイトルの通りで、画像を別のデータの入れ物にするというものだ。

一般的なフルカラー画像は、RGB 各色の情報を 0〜255 の 8-bit の数値として保持している。この各色それぞれの 8-bit 目の 0 or 1 を入れ替えたとしても、自然画像の場合は、ほとんど見た目は変わらない。例えば、下の1番左の画像の、RGB 各色の 8-bit 目をすべて 0 にしたものが中央の画像なのだが、この違いが目視で即座にに分かる人はほとんどいないだろう。いちばん右は、両者の差を極端にしたもので、実際には左と中央の画像はこのように違うものなのだ。

Row[{neko = Import["http://blog.hulinks.co.jp/2010/12/mathe-neko.png"],
  trNeko = Image[BitAnd[ImageData[neko, "Byte"], 2^^11111110], "Byte"],
  ImageAdjust[ImageSubtract[neko, trNeko]]}, " "]
元画像各色末尾の1-bitをゼロクリアした画像差分を極端化したもの

この「各色末尾の 1-bit」を、画像全体にわたって結合すると、実は結構な容量になる。上の画像はたかだか 180×240pixel の画像なのだが、

180 × 240 × 3colors/pixel ÷ 8bit/byte = 16200bytes

と、実に 16KB 弱のデータの格納場所に使うことができる。

コンセプト

テキストなら、16KB はずいぶんの量だ。例えば、前回の僕の記事ページのテキスト(HTML を外してプレーンテキストのみにしたもの)は、7KB 弱なので、上のネコ画像にじゅうぶん納められる。ちょっとやってみよう。

まず、入れ物としての画像にデータを納める関数と、データが納められた画像からデータを抽出する関数を定義する(このコードは、使っているシンボル名などが少し違うものの、上記リンクの元記事のものと同じ内容)。

(* 格納する関数の定義 *)
insertData[carrier_Image, data_String] :=
  Block[{crData, trCarrier, pixelChannels, dataBits},
    crData = ImageData[carrier, "Byte"];
    pixelChannels = Apply[Times, Dimensions[crData]];
    dataBits =
      PadRight[Flatten[IntegerDigits[ToCharacterCode[data], 2, 8]],
        pixelChannels];
    trCarrier = BitAnd[crData, 2^^11111110];
    Image[trCarrier +
      Fold[Partition, dataBits,
        Reverse@Rest[Dimensions[crData]]], "Byte"]]

(* 抽出する関数の定義 *)
extractData[img_Image] := Block[{data},
  data = BitAnd[ImageData[img, "Byte"], 1];
  data = Partition[Flatten[data], 8];
  data = FromCharacterCode[FromDigits[#, 2] & /@ data];
  StringTrim[data, FromCharacterCode[{0, 0, 0, 0, 0, 0, 0, 0}] ..]]

関数が定義できたら、先頭で使った変数 neko の画像を入れ物に、前回の僕の記事を納めてみる。

imgWithData = insertData[neko, ExportString[Import[
  "http://blog.hulinks.co.jp/2010/11/getting-coordinates-by-mathematica.html", "Plaintext"], "Text"]]

ブログ記事入り画像

見た目は元の画像と変わらないが、この画像には、前回の記事が入っている。取り出してみよう。

ImportString[extractData[imgWithData], "Text"]

画像からテキストを抽出

この記事をご覧の読者の方が、上に貼った画像をブラウザからダウンロードして抽出操作だけをしても、同じテキストが確認できるはずだ。
おっと、 書き忘れたが、 入れ物として使ってデータを納めた画像は、 データを破壊しない PNG などのファイルタイプで保存する必要があることに注意。 自然画像でよく使われる JPEG は基本的に破壊圧縮なので、 データを納めた画像を JPEG にすると、 納めたデータは失われる。

ちなみに、16KB あれば、例えば、小さな JPEG ファイルであればじゅうぶん格納できる。下の画像には、別の JPEG ファイルが納められている。

JPEGデータを納めた画像

ImportString[extractData[imgWithData2], "JPEG"]

画像から画像を抽出する

これを応用すれば、画像にひもづくメタデータのバックアップをすべてサムネール PNG 画像に納めておいて、必要に応じてサムネールからメタデータを復元するなんて仕組みを作ることも、原理的には可能なはずだ。

もちろん、このコンセプト自体は、別に Mathematica でなくても実現可能で、バイナリデータとビット列を扱うことができれば、どんな言語でも、同じことができる。ただ、アイデア試行錯誤の段階でこの手のことを試すのであれば、上で紹介した程度の小さなコードで済む Mathematica は、やはりツールとしてはダントツに便利だと、僕は思うのだ。

※この記事の内容は執筆者の個人的見解で、ヒューリンクスによる公式情報ではありません。[免責事項]

トラックバック

この記事へのトラックバックURL
http://blog.hulinks.co.jp/cgi/mt/mt-tb.cgi/448
内容に対しての関連性がみられないものは削除する場合があります

コメント一覧

早速上記サンプルを試してみました!面白いですね。
「電子透かし」はこのような原理で作られているんでしょうか。

さっそく試して下さってありがとうございます。
そうですね。これは、最も簡単な「電子透かし」の実装に相当するのだと思います。実際の電子透かしでは、画像のトリミングなどでも埋め込みデータを失わないようにするとか、いろいろ工夫されてるんじゃなかったかと思います。

コメントの投稿

Emailアドレスは表示されません。は必須項目です。
ヒューリンクス取り扱い製品の内容や購入に関するお問い合わせはヒューリンクスサイト連絡先へお願いいたします。投稿前にその他の注意事項もご覧ください。

HULINKS サイトの新着情報