画像を数値で置き換える方法

[上に] [前に] [次に]
ピコ 1999/07/16(金) 00:03:34
ちょっとした画像を表示したいと思っているのですが、
gif画像を取り込むのではなく、00、ffなどのデータでperlスクリプトの中に書いているのをどこかで見たことがあります。
これはどのようにすれば、できるのでしょうか。
変換法みたいなものがあれば教えて下さい。

B-Cus 1999/07/16(金) 00:07:01
事前に画像ファイルを読んで
 s/(.)/sprintf("%%%02lX",unpack("C",$1))/eg;
でテキストに変換しておいて、実行時は
 s/%([0-9a-fA-F][0-9a-fA-F])/pack("C",hex($1))/eg;
でバイナリに戻せばいいんでない? 試してないけど。

ふじ 1999/07/16(金) 01:42:29
Base64(メールの添付によく使われる方式)でエンコード、という手も。

MIME::Base64 モジュールか、
mime_pls ( http://www.cc.rim.or.jp/~ikuta/mime_pls/ )
を使うと簡単です。

#Perlスクリプトの中に書いておくのと、ファイルにしておくのでは
#どっちが効率いいのかは知りませんが。

ふじ 1999/07/16(金) 03:40:19
ちょっと実験してみました。

1.URLエンコード、デコード
s/(.)/sprintf("%%%02X",unpack("C",$1))/eg;
で、エンコード
s/%([0-9a-fA-F][0-9a-fA-F])/pack("C",hex($1))/eg;
で、デコード。

2.Base64URLエンコード、デコード
MIME::Base64 モジュールを使用。

12KBのGIF画像をテキストに変換したものをPerlスクリプト内に記述して
変数に格納、それをデコードしてバイナリファイルに書き出す、という
スクリプトをそれぞれ Benchmark モジュールでベンチを取りました。

(結果)

1.の方法
バイナリ1バイトをエンコードすると3バイトになるので、
サイズが3倍になります。また、改行コードが入らないので
テキストエディタで扱いにくいです。
#エンコード時に一定サイズごとに改行を挟めばいいかも知れない。

デコードが遅いです。
> s/%([0-9a-fA-F][0-9a-fA-F])/pack("C",hex($1))/eg;
これでデコードすると、1000回ループで
156 wallclock secs (156.81 usr +  0.00 sys = 156.81 CPU)

> s/%([0-9a-fA-F][0-9a-fA-F])/chr(hex($1))/eg;
これで
138 wallclock secs (137.70 usr +  0.00 sys = 137.70 CPU)
でした。
#システム時間が0.00なのは、OSのキャッシュが効いていて
#ディスクアクセスをしていないからだと思います。

2.の方法
サイズは4/3に増えます。
76文字ごとに改行が入るので扱いやすいです。
デコードは、
decode_base64($data);
で、1000回ループで
4 wallclock secs ( 3.84 usr +  0.00 sys =  3.84 CPU)
でした。
1.の方法よりも30倍以上程度高速です。

#上記時間は K6-2(400), 128MB, Win95, Perl 5.005_3で測定しました。

つーことで、Base64の方がお勧めかと。長文失礼。

ピコ 1999/07/17(土) 06:57:39
ありがとうございます。
画像ファイルが小さいことと、別ファイルを用意する手間を省きたいという
理由から、1の方法でやってみました。
以下の方法では、エンコードしたデータファイル($log)を呼び出し、デコード処理して保存しているのですが、
この$log2ファイルを呼び出してprint "<img src=$log2>";とすると確かに表示されました。

open(IN,"$log") || &error;
@IN = <IN>;
close(IN);

foreach (@IN) {
$_ =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C",hex($1))/eg;
push (@new_data,$_);
}
open(LOG2,">>$log2") || &error;
print LOG2 @new_data ;
close(LOG2);


しかし、@new_dataから直接画面に表示する方法が、まだよく分からないでいます。

@data = (
"47","49","46","38","39","61","16","00","0a","00","b3","02","00","00","00","00","ff","d6","00","ff",
"ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff","ff",
中略
"22","00","3b");

print "Content-type: image/gif\n\n";
foreach (@data) { $data = pack('C*',hex($_)); print $data; }
exit;

のようにしているプログラムを見かけたので、試してみましたが、
どうもうまくいきません。

B-Cus 1999/07/17(土) 07:49:05
逆。

それは画像ファイルの中の %xx を探してデコードしようとしてるだけ。
普通画像の中にそういうコードはないから、何も変換してないわけ。
その結果が表示できるのは当然。

事前に
 open(IN,"image.gif") || die;
 while (read(IN,$_,10)){
  s/(.|\n)/sprintf("%%%02lX",unpack("C",$1))/eg;
  print "$_\n";
 }
 close(IN);
とすると、
 %47%49%46%38%39%61%17%00%16%00
 %C4%00%00%2C%11%0D%4E%11%07%6F
 %15%07%72%26%1A%52%28%21%64%37
 %30%67%43%3D%62%5B%5A%91%1A%07
 …略…
という出力が得られるでしょ?

それをスクリプト中に貼るわけ。
 $data=<<END;
 %47%49%46%38%39%61%17%00%16%00
 %C4%00%00%2C%11%0D%4E%11%07%6F
 …略…
 END
てな感じ。

で、
 print "Content-type: image/gif\n\n";
 foreach (split("\n",$data)) {
  s/%([0-9a-fA-F][0-9a-fA-F])/pack("C",hex($1))/eg;
  print $_;
 }
とするとデコードして画像が出力される、と。

# ところで
#  s/(.)/sprintf("%%%02lX",unpack("C",$1))/eg;
# だと . が \n にマッチしないんですが、何か対策あります?
# バイナリを扱ってると、\nを特別扱いしてほしくないので。

全体を通して、改行コードの扱いに注意してください。

これで大体の考え方がわかったら MIME版に挑戦してみては。

B-Cus 1999/07/17(土) 07:54:25
> #  s/(.)/sprintf("%%%02lX",unpack("C",$1))/eg;
> # だと . が \n にマッチしないんですが、何か対策あります?
s/(.)/sprintf("%%%02lX",unpack("C",$1))/egs でうまくいきました
(最後のsフラグね)。

ピコ 1999/07/18(日) 00:10:22
[[解決]]
ありがとうございます。うまくいきました。
1kのファイルをエンコードすると、やはり3kぐらいになりましたが、
さほど表示に時間はかからなかったので、この方法でいきたいと思います。
大きなファイルを扱う状況になったら、Base64にチャレンジしてみます。

B-Cus 1999/07/18(日) 00:21:03
まぁ一応デコード部を
 foreach ( split("\n",$data) ) {
  s/(..)/pack("C",hex($1))/eg;  # %を無くしたのでデータの互換性はないよ。
  print $_;
 }
にでもしときんさい。ちーとは速くなるでっしゃろ。計ってないけど。

エンコード部は1度しか使わないからどーでもいいけど、気になるなら
 while (read(IN,$_,30)){
  foreach ( split("",$_) ){
   $data .= sprintf("%02lX",unpack("C",$_));
  }
  print "$data\n";
 }
とかね。

mm 1999/07/18(日) 15:31:20
もう解決しちゃってますが…
CGIのperl スクリプトにバイナリデータを直接貼り付ける話ですよね。

小さいGIF画像なら、スクリプト内に、
 print "";
と書いておいて、エディタで "" 内にGIFファイルを直接読み込み、
改行とか \ とか " や $,@ などを手で \x0a とかに適当に書き換えても
大した手間ではないような気もしますが…
これなら、デコードの必要もないですし。

バイナリデータが大きい場合は、Windows環境なら↓みたいな感じかな…

open(GIF, "hoge.gif") || die;
open(FH, ">hoge.cgi") || die;
binmode(GIF);
$gif = join('',<GIF>);
close(GIF);
@lines = ();
$gif =~ s/.{1,100}/push(@lines,$&)/esg;
print FH "print \"Content-type: image/gif\\n\\n\";\n";
print FH "print ";
foreach (@lines) {
s/["\$\@\\]|[\x81-\x9f\xe0-\xfc][\x41-\x5b\x5d-\x7e\x80-\xfc]|[\x00-\x1f\x7f-\xa0\xe0-\xff]/length($&)>1?$&:sprintf("\\x%02X",unpack("C",$&))/esg;
print FH "\"$_\" .\n";
}
print FH "\"\";\n";
close(FH);

ピコ 1999/07/18(日) 21:35:36
>小さいGIF画像なら、エディタで "" 内にGIFファイルを直接
>読み込み、改行とか \ とか " や $,@ などを手で \x0a とかに
>適当に書き換えても大した手間ではないような気もしますが…

そういうことってできるんですか?
ためしにmmさんの方法でやってみましたが、
変な漢字や半角かなが出てきてしまいました。

>Windows環境なら↓みたいな感じかな…
というのは、AN HTTPDのようなものを使ってローカルで
作るということかな??

mm 1999/07/18(日) 23:31:11
>そういうことってできるんですか?
できるハズですが、バイナリを扱えなかったり、\rと\nを区別できないエディタ
の場合はムツカしいかも知れません…メモ帳ではムリかな?

>というのは、AN HTTPDのようなものを使ってローカルで作るということかな??
上のスクリプトを hoge.gif と同じフォルダに置いて、perl for Win32 で実行すれば、
hoge.cgi が作成されるので、ここから適当にカットアンドペーストすればよいという
ことです。

ピコ 1999/07/19(月) 00:20:50
解決済みマークを付けているので、このまま続けて書くのは
心苦しいのですが……

久しぶりにAN HTTPDを起動してみましたが、やはり、
print "Content-type: image/gif\n\n";
print "GIF89a\x0F\x00\x13\x00\x83\x00\x00\x00\x003\x1B\x1BJ77`TTw血、ゥゥサナナメ矣\xE8……
みたいになります。
使い方がまちがっているのかも。

エディタはQXなのですが、直接編集することはできますか?
\x0aのようなコードはどこで調べれば良いのでしょうか。

mm 1999/07/19(月) 02:41:17
>使い方がまちがっているのかも。
hoge.cgi の中身がそのようになったのであれば、使い方は間違ってはいません。

説明が後になってしまいましたが、私の方法は、画像データを %47%49%46… のように
エンコードする代わりに、\x47\x49\x46… のようにエンコードして、
これを "" で囲むことにより、perlのコンパイル時にデコードさせるように
しているに過ぎないのです。
ただ、このままだと画像データのバイト数が4倍に増加するので、
Windowsのエディタで普通に編集可能な印字可能文字は、エンコードせずにそのまま
にしています。このため、アスキー文字の他に漢字や半角カナも混ざるのです。

AN HTTPDを起動されたのなら、ブラウザでこの hoge.cgi を呼び出してみれば、
hoge.gif の画像が表示されるハズなのですが…

これがうまく表示できたなら、hoge.cgi の内容をピコさんのperlスクリプトの
適当な場所にコピーすればいいと思います。

>エディタはQXなのですが、直接編集することはできますか?
直接編集は、かなりきわどい操作が必要で、ある程度こういうことに慣れた
人でないと難しいと思います。
残念ながら、私はQXというエディタは知らないので、この件はパスさせて下さい。

>\x0aのようなコードはどこで調べれば良いのでしょうか。
えっと、アスキーコード表などを見れば分かると思いますが…そういうコトではないのかな?

ピコ 1999/07/19(月) 23:41:02
[[解決]]
hoge.cgiを起動するというところで勘違いしていました。
無事表示されました。
直接編集は、もう少し勉強してからにしたいと思います。
ありがとうございました。

[上に] [前に] [次に]