« ◇iTunesのアートワークをAAC(.m4a)ファイルに含める | トップページ | ◇WalkmanのSDにfoobar2000DLNA同梱 »

◆JPEGのbaseline判定ツール(jpeg構造解析ソース付き)

 JPEGベースライン形式とプログレッシブ形式

JPEGにはベースライン形式とプログレッシブ形式があります。

同じJPEGでも必ずしも全てのプログラムが取り扱える訳ではなく、例えばSONYのwalkmanではアートワーク画像がベースライン形式のJPEGまたはPNGである必要があり、プログレッシブ形式のJPEGの場合表示することが出来ません。

JPEG画像がベースラインかプログレッシブかを確かめる単純なツールを作成しました。PGNの判別もします。

jpgBLineChk.zip をダウンロードし、展開後、A05_run.batを起動すれば、Pictureフォルダ内のファイルをチェックし、ファイル名の後にPROGRESSIVEかBASE_LINEかを示します。1行に1ファイル、で複数行になります。 (JAVA8の実行環境が必要です)

mull.png: PNG
R-1000405-1202393759_jpeg.jpg: PROGRESSIVE
R-3344447-1326656338_jpeg.jpg: BASE_LINE
zamp27.png: PNG
四季 _I MUSICI 1.jpg: BASE_LINE

◇iTunesのアートワークをAAC(.m4a)ファイルに含めるネットから画像を拾って設定 作業ではとても重宝しました。殆どはBASE_LINEですが、たまにPROGRESSIVEがあります。

単純なものですのでソースコードを提示します。

 JPEGベースライン形式判定ツール

JPEGは次の形式をしています。

SOIは0xFFD8、EOIは0xFFD9です。
セグメントは先頭に0xFF、次に1バイトのセグメント種別が続き、その後ろに2バイトの長さ情報があります。長さ情報の後ろに「長さ情報-2」バイトの内容が置かれます。
SOIの後、複数のセグメントがあり、最後がSOSセグメントとなります。SOSセグメントの後ろには画像データがあります。 画像データは長さデータで修飾されておらず、内容を解析しないと終端は分かりません。
画像データの後ろにEOIが置かれますが現実的には意味を持ちません。

セグメント種別でベースライン、プログレッシブの判断ができます。

0xC0 : ベースライン
0xC1 : シーケンシャル
0xC2 : プログレッシブ
0xC3 : 可逆DPCM

これ以外にも多くのセグメントがありますが、他は無視します。

プログラムはSOIを確認後、セグメント単位で読み飛ばしながら、0xC0~0xC1のセグメント出現で判断し、結果を戻します。

// JpgBLineChk.java
import java.io.*;
public class JpgBLineChk {
   final static boolean D=false;// デバグフラグ
   public enum Type{
       BASE_LINE,
       SEQUENCIAL,
       PROGRESSIVE,
       DCPM,
       PNG, // JPEGではないがチェックする
       UNKOWN
       };
   public static Type getType(BufferedInputStream bis_)throws Exception{
      byte[] _buf2 = {0,0};     // SOI取得用
      byte[] _buf4 = {0,0,0,0}; // SEGMENTマーカーと長さ取得用
      // SOIの確認
      if( bis_.read(_buf2)!=2 ) return Type.UNKOWN;
      if( (_buf2[0]&0xff)!=0xFF || (_buf2[1]&0xff)!=0xD8 ){
         // PNGか調べる
         if( (_buf2[0]&0xff)==0x89 && (_buf2[1]&0xff)==0x50 ){
            byte[] _buf6={0,0,0,0, 0,0};
            if( bis_.read(_buf6)!=6 ) return Type.UNKOWN;
            if(   (_buf6[0]&0xff)==0x4E && (_buf6[1]&0xff)==0x47 
                &&(_buf6[2]&0xff)==0x0D && (_buf6[3]&0xff)==0x0A 
                &&(_buf6[4]&0xff)==0x1A && (_buf6[5]&0xff)==0x0A  ){
               return Type.PNG;
               }
            }
         return Type.UNKOWN;
         }
      while(true){
         // セグメントマーカー確認
         if( bis_.read(_buf4)!= 4  ) return Type.UNKOWN;
         if( (_buf4[0]&0xff)!=0xFF ) return Type.UNKOWN;
         long _slen = (long)(((_buf4[2]&0xFF)<<8)+(_buf4[3]&0xFF)-2);
         if(D) System.out.printf("SEGMENT:%02x%02x len:%d\n"
                                ,_buf4[0],_buf4[1],_slen);
         switch( _buf4[1]&0xff ){
         case 0xC0: return Type.BASE_LINE;
         case 0xC1: return Type.SEQUENCIAL;
         case 0xC2: return Type.PROGRESSIVE;
         case 0xC3: return Type.DCPM;
         case 0xDA: return Type.UNKOWN;//SOS(この後ろはSEGMENTではない)
            }
         // セグメント読み飛ばし
         long _len;
         while( _slen>0 && ((_len=bis_.skip(_slen))!=-1) ) _slen -= _len;
         }
      }
   public static Type getType(File file_)throws Exception{
      try(BufferedInputStream _bis = new BufferedInputStream(
                                       new FileInputStream(file_));){
         return getType(_bis);
         }
      }
   public static void main(String[] args_){
      for(String _arg:args_){
         try{
            File[] _files= new File(_arg).listFiles();
            if( _files!=null ){
               for(File _file:_files){
                  try{
                     System.out.println(_file.getName()+": "
                                        +JpgBLineChk.getType(_file));
                     }
                  catch(Exception _ex){
                     System.out.println(_ex);
                     }
                  }
               }
            }
         catch(Exception _ex){
            System.out.println(_ex);
            }
         }
      }
   }

このプログラムでは先頭が0xFFD8でない場合補足的にPNGではないかのチェックも行っています。

main部はコマンド引数としてフォルダ名を受け、フォルダ内のファイルに関してチェックを行い結果を表示するものとなっています。

補足:
 _bis.read(_buf)は長さ分読むのでくりかえしの必要はありません。
 _bis.skip(_skip)は短い場合もあるので繰り返さなくてはなりません。
 _bis.skip(_skip)は最後まで到達した後は-1を返します。

 実行バッチ

実行用のバッチファイルA05_run.batは次のようになっています。

 実行用バッチ

:: A05_run.bat
@echo off
set DIR=%UserProfile%\Pictures
set MAIN=JpgBLineChk
set JAVA=java.exe
"%JAVA%" -cp . %MAIN% %DIR%
pause

この例では変数DIRにユーザのMyPictureフォルダを与えています。
この部分を必要に応じ変更してください。

 実行例

実行例を示します。(長いので一部抜粋)

71mnczFjxrL__SL1200_.jpg: BASE_LINE
81UnrBT5hgL__SL1200_.jpg: BASE_LINE
91jo6WhblIL__SL1418_.jpg: BASE_LINE
desktop.ini: UNKOWN
mull.png: PNG
R-1000405-1202393759_jpeg.jpg: PROGRESSIVE
R-3344447-1326656338_jpeg.jpg: BASE_LINE
R1202393759.jpg: PROGRESSIVE
R12_baseline.jpg: BASE_LINE
zamp27.png: UNKOWN
四季 _I MUSICI 1.jpg: BASE_LINE
惑星 _Previn_Royal Phil.Orc..jpg: BASE_LINE
無題.png: PNG
続行するには何かキーを押してください . . .

これらは先の記事 ◇iTunesのアートワークをACC(.m4a)ファイルに含める の「ネットから画像を拾って設定」でiTunesを介してwalkmanに設定したアートワークファイルです。

赤がwalkmanでアートワーク表示できなかったファイル青はPhotoShopでBASE_LINEに変換保存しwalkmanで表示できるようになったファイルです。

 ソースと再ビルド

jpgBLineChk.zip にはソースコードとビルド用のバッチも入れてあります。

Java8の実行環境が必要です。

JDK8以降があれば、ソースに変更を加えて再ビルドすることができます。

 ビルド用バッチ

:: A01_build.bat
@echo off
set SRCS=JpgBLineChk.java
set JAVAC=javac.exe
"%JAVAC%"  -Xlint:unchecked -encoding utf8 %SRCS%
if ERRORLEVEL 1 goto ERR
echo === OK ===
goto END
:ERR
echo === SOME ERROR OCCURED ===
:END
pause

 クリア用バッチ(の見本)

:: A00_clear.bat
del /q *.class > NULL 1>&2
pause

|

« ◇iTunesのアートワークをAAC(.m4a)ファイルに含める | トップページ | ◇WalkmanのSDにfoobar2000DLNA同梱 »

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック


この記事へのトラックバック一覧です: ◆JPEGのbaseline判定ツール(jpeg構造解析ソース付き):

« ◇iTunesのアートワークをAAC(.m4a)ファイルに含める | トップページ | ◇WalkmanのSDにfoobar2000DLNA同梱 »