こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

JPEGの縦横サイズ取得について。

立て続けの質問をお許しください。
JPEGの縦横サイズを取得するアルゴリズムを考えているのですが、以下のページを参考にしています↓
http://tohoho.wakusei.ne.jp/lng/200003/00030402.htm
縦横サイズの情報を探すためには、
***上のURLより引用*****************************
JPEG(ベースラインJPEGの場合)ファイルの場合SOFn マーカー FFC0 (Hex) を見つけてここから始まる.SoF0 セグメント FF の位置を0 として相対オフセット、位置 5 から、2Bytes に高さ 7 から 2Bytesに幅
****引用終わり*******************************
とある通り、マーカーFFC0を探さなければいけないらしいのですが、自分なりに色々調べながらマーカー位置を探すのに以下のような処理を考えました。

open(IN, "aaa.jpg") || die;
binmode IN;
while (1) {
 if(getc(IN) == 0xFF){
  if(getc(IN) == 0xC0){last;}
}
}

上の処理は、FFC0が見つからなかったときにループを抜ける処理をかいていませんが、見つかればループは抜けるはずなので、それは書いてません。
ですが、こう書いても無限ループに陥ってしまいます。
なにがいけないやら分かりません。
どこがいけないでしょうか。

投稿日時 - 2004-03-19 14:33:36

QNo.809857

暇なときに回答ください

質問者が選んだベストアンサー

#1です。

>1:SOI, EOI, RSTmの場合は2byteだけスキップ。
>ではなくて、「その次のバイトにスキップ」です。

そうですね。実際には、チャックしたマーカーの次のマーカーの位置に進んでますから、そのまま何もせず、次のマーカーの読み込みループに戻る事になりますね。

>こんな感じになるのでしょうか?また、セグメントの長さとは最初
>の方で書いた、....の長さということでしょうか?

マーカーの次のレングスデータは、レングスデータそのものも含めた長さです。

レングス付きセグメント(SOI, EOI, RSTmじゃないと判定した時)は、マーカーに続く2バイトのレングスを拾い、レングスデータの次に進んでいるので、実際にスキップする長さは「拾ったレングス-2」になりますね。

あと、注意しないといけないのは、マーカーの次のセグメント長が「2」になってる場合。例えば、以下のようなファイルイメージ。

SOI :FF D8
APP0:FF E0 00 10 4A 46 49 46 00 01 02 01 00 48 00 48 00 00
COM :FF FE 00 02
APPD:FF ED ....

COMセグメントを見付け、レングスデータの「2」を拾った後、読み込み位置は次のAPPDセグメントの所に居ます。

スキップすべきバイト数を「拾ったレングス-2」から計算し「0」になった場合、読み込み位置は次のセグメントの所になっているので、実際のスキップ処理はせず、次のマーカーの読み込みループに戻る事になります。

「コメントデータ無しでJPEG保存をすると、コメントが無くても無理矢理必ずセグメント長2のCOMセグメントを付けて保存する」と言う画像エディタがあって、これのおかげで「0バイトスキップしようとして65536バイトスキップしちゃった」と言うバグを作った事があります。

投稿日時 - 2004-03-20 12:56:21

お礼

ありがとうございます~
無事解決できました!
分かりやすいアドバイス助かりました^^
またよろしくおねがいします。

投稿日時 - 2004-03-23 01:51:06

このQ&Aは役に立ちましたか?

2人が「このQ&Aが役に立った」と投票しています

回答(3)

ANo.2

>  if(getc(IN) == 0xFF){

Perlのgetc()で得られるのは、文字列オブジェクトです。
Cと違って、数値として直接比較する事はできません。

perl -e 'print "a" eq "\x61" ? "OK\n" : "NG\n"'

・・のように記述してみては。

投稿日時 - 2004-03-20 07:35:05

お礼

ありがとうございます~。
無事解決いたしました~

投稿日時 - 2004-03-23 01:50:11

ANo.1

無限ループの件はちょっと判りませんが、セグメントのスキップを正しく行わないと、思った通りのデータが拾えない場合があります。

スキップを正しく行わない場合、例えば、SoF0セグメントの前にリマークセグメントがあって、リマークデータの中に FF C0 のデータ並びがあると、プログラムは間違った結果を返すでしょう。

例えば、以下のような場合。

FF D8 FF EE 00 04 FF C0 FF E0 00 10 …

これは、
FF D8
FF EE 00 04 FF C0
FF E0 00 10 …
の3つのセグメントが並んでいますが、2番目のセグメント内の「FF C0」はセグメントマーカーではなく、リマークデータです。セグメントスキップを正しく行わないと、リマークデータとしての「FF C0」をマーカーと誤解します。

なお、リマークデータ以外に、セグメント内のデータには「FF xx」の組み合わせのデータが「データとして」出現する場合もあり、これもマーカーと誤解する原因になります。

実際に「リマーク内にマーカーと似たデータが居る」ようなJPEGファイルは、PhotoShopでサムネイル付きJPEGファイルを保存した場合に作成されます。

PhotoShopのJPEGのサムネイルデータはサイズが小さくなったJPEGファイルイメージで、リマークセグメントのリマークデータとしてJPEGファイル内に付加されます。

強引な言い方をすれば「JPEGファイル内に、小さいJPEGが入れ子になっている」と言えます。

JPEGのセグメント解析は「先頭から順サーチ」では出来ないので、もう少し工夫が必要です。

投稿日時 - 2004-03-19 15:34:19

補足

さっそくのご回答ありがとうございます。
http://www02.so-net.ne.jp/~koujin/jpeg/JpegMarker.html
を一読し、またプログラムを組んでいるところです。
アドバイスを頂いて、先頭から検索する危険さがなんとなく分かってきました。上のURLとchie65536さんのアドバイスより、解釈したのは、
***********************************
セグメントの形式には2種類あって、
FF XX
の形式と、
FF XX YY ZZ ....
の形式(YY ZZ はそのセグメント本体の長さの情報が入っている)

あるセグメント内の上の....以降でFF C0が出てしまったらこいつに引っかかってしまう。

だからセグメントごとにスキップする必要があって、
FFを見つけたら、
1:SOI, EOI, RSTmの場合は2byteだけスキップ。
2:それ以外の場合で、FFの後ろがC0でない場合、さらに後ろの2バイトを読み込み、そのセグメントの情報を得て、そのバイト分だけスキップ。
3:1,2の動作を繰り返し、FFの後ろがC0になったら
めでたくSOFnの発見


こんな感じになるのでしょうか?また、セグメントの長さとは最初の方で書いた、....の長さということでしょうか?

投稿日時 - 2004-03-20 02:19:15

お礼

すいません。回答の補足に関する訂正です。
>1:…2byteだけスキップ

ではなくて、「その次のバイトにスキップ」です。

投稿日時 - 2004-03-20 02:47:36

あなたにオススメの質問