グレースケールのJPEGをBitmapに変換するには?


jpeg  2003-12-03 12:59:55  No: 52722  IP: [192.*.*.*]

グレースケールのjpegをbmp(dib)に変換する際、RGB変換の所で
ハンドルエラーが出るのですがカラーとグレースケールで変換の
仕方が違うのですか?

編集 削除
Gak  2003-12-03 15:52:38  No: 52723  IP: [192.*.*.*]

> カラーとグレースケールで変換の仕方が違うのですか
違います。
jpegから得られるピクセルデータは、カラーの時は 3Byte/1Pixel ですが
グレースケールの時は 1Byte/1Pixel です。
よって、上記の点を考慮しないとダメです。

編集 削除
jpeg  2003-12-03 16:03:45  No: 52724  IP: [192.*.*.*]

ということは成分数を1に設定するということですか?
JPEG画像データの幅の変更するということですか?

編集 削除
Gak  2003-12-03 16:38:57  No: 52725  IP: [192.*.*.*]

LPBYTE lpJpegPixel;  //  Jpegから取得したPixelデータ
  LPBYTE lpDibPixel;   //  bmp(dib)のPixelデータ格納位置

  if (グレイスケール) {            //  成分数?==1
      lpDibPixel++ = lpJpegPixel;  //  Blue要素格納
      lpDibPixel++ = lpJpegPixel;  //  Green要素格納
      lpDibPixel++ = lpJpegPixel;  //  Red要素格納
      lpJpegPixel++;               //  次へ
  }
  else {                              //  成分数?==3
      lpDibPixel++ = lpJpegPixel[2];  //  Blue要素格納
      lpDibPixel++ = lpJpegPixel[1];  //  Green要素格納
      lpDibPixel++ = lpJpegPixel[0];  //  Red要素格納
      lpJpegPixel += 3;               //  次へ
  }

これで感じ掴めます?

編集 削除
jpeg  2003-12-03 16:47:33  No: 52726  IP: [192.*.*.*]

この場合どうなりますか?

int m_iHSize;    // JPEG画像データの幅
int m_iVSize;    // JPEG画像データの幅

u_char* m_pRedP;  // 赤の要素のビットデータ
u_char* m_pGreenP;  // 緑の要素のビットデータ
u_char* m_pBlueP;  // 青の要素のビットデータ
  
LPBYTE pBitData = new BYTE[dwHSize*m_iVSize];

LPBYTE pCurrentBit = pBitData;    //Jpegから取得したPixelデータ
LPBYTE pRed  = m_pRedP;
LPBYTE pGreen  = m_pGreenP;
LPBYTE pBlue  = m_pBlueP;

  for(int y=m_iVSize-1; y>=0; y--){
    pCurrentBit = pBitData + (y*dwHSize);
    for(int x=0; x<m_iHSize; x++){
      *pCurrentBit++ = *pBlue++;
      *pCurrentBit++ = *pGreen++;
      *pCurrentBit++ = *pRed++;
    }
    for(x=0; x<iHGap; x++){
      *pCurrentBit++ = 0;
    }
  }

編集 削除
Gak  2003-12-03 17:16:27  No: 52727  IP: [192.*.*.*]

示してくれたソースでは不明な点があり答えにくいです。
> u_char* m_pRedP;    // 赤の要素のビットデータ
> u_char* m_pGreenP;    // 緑の要素のビットデータ
> u_char* m_pBlueP;    // 青の要素のビットデータ
例えば、この中身はどのようにして取得したデータですか?

ちょっと確認ですが、自分は
  ・jpeg -> bmp(dib) への変換をしたい
  ・カラー(グレースケールで無い)Jpegの変換はできている
という認識をしていますが合っていますか?
無駄な事を書いてしまってもアレなので…一応の確認です。

編集 削除
jpeg  2003-12-03 17:42:21  No: 52728  IP: [192.*.*.*]

> u_char* m_pRedP;    // 赤の要素のビットデータ
> u_char* m_pGreenP;    // 緑の要素のビットデータ
> u_char* m_pBlueP;    // 青の要素のビットデータ
例えば、この中身はどのようにして取得したデータですか?

下記のプログラムからR成分・G成分・B成分を取り出しました。
  int      ux,                    // 横ユニット位置
  int      uy )                  // 縦ユニット位置
{
  int*  yp = mCompP[0];                // Y
  int*  up = mCompP[1];                // Cb
  int*  vp = mCompP[2];                // Cr
  int    numLine = uy * mVMax * 8;          // ライン:縦ユニット数×ユニットライン数
  int    offsetV = numLine * mProperty.HSize;    // 縦オフセット:ライン×1ラインドット数
  int    offsetH = ux * mHMax * 8;          // 横オフセット:横ユニット数×ユニット内横ブロック数×ブロック内ドット数
  int    offset = offsetV + offsetH;          // 総合オフセット
  u_char*  rp = mRgbP[0] + offset;            // 赤書き込み位置
  u_char*  gp = mRgbP[1] + offset;            // 緑書き込み位置
  u_char*  bp = mRgbP[2] + offset;            // 青書き込み位置
  int    endX = mHMax*8;                // 横ループ終了ポイント
  int    endY = mVMax*8;                // 縦ループ終了ポイント

  for( int picY=0; picY<endY; picY++ ){
    for( int picX=0; picX<endX; picX++ ){
      if( picX + offsetH >= mProperty.HSize ){  // 付加ビットならば
        yp += endX - picX;            // ソースアドレスを更新
        up += endX - picX;
        vp += endX - picX;
        break;                  // 次のラインへ
      }

      int  index = picY * mProperty.HSize + picX;  // 書き込み位置

#ifdef GRAY
      gp[index] = bp[index] =  rp[index] = ReviseValue( *yp++ );
#else
  double  v = *yp              + (*vp - 0x80) * 1.4020;
      rp[index] = ReviseValue( v );        // R
      v = *yp - (*up - 0x80) * 0.3441  - (*vp - 0x80) * 0.7139;
      gp[index] = ReviseValue( v );        // G
      v = *yp + (*up - 0x80) * 1.7718;
      bp[index] = ReviseValue( v );        // B

      yp++; vp++; up++;
#endif
    }  // picX
  }  // picY


ちょっと確認ですが、自分は
  ・jpeg -> bmp(dib) への変換をしたい
  ・カラー(グレースケールで無い)Jpegの変換はできている
という認識をしていますが合っていますか?

上記解釈で合っています。

編集 削除
Gak  2003-12-03 19:20:37  No: 52729  IP: [192.*.*.*]

ソース全体を把握しているワケでないのでかなり憶測が入りますが…。

上記ソース中の mHMax、mVMax には、水平/垂直のサンプリングファクタ(の最大値)を
取得しているのだと思われます。
グレイスケールの場合は mHMax、mVMax(水平/垂直のサンプリングファクタ)
の値を[ 1 ]として処理してみて下さい。

編集 削除
jpeg  2003-12-08 09:14:05  No: 52730  IP: [192.*.*.*]

上記のソースでmHMax、mVMax を1に設定しても症状は変わりませんでした。
デバッグしてみますと、RGBのハンドルエラーが出てしまいました。
下記のソースの中で*pBlue++に何も格納されていませんでした。(*pGreen++と*pRed++には格納されている。)
for(int y=m_iVSize-1; y>=0; y--){
        pCurrentBit = pBitData + (y*dwHSize);
        for(int x=0; x<m_iHSize; x++){
            *pCurrentBit++ = *pBlue++;
            *pCurrentBit++ = *pGreen++;
            *pCurrentBit++ = *pRed++;
        }
        for(x=0; x<iHGap; x++){
            *pCurrentBit++ = 0;
        }
    }

編集 削除
Gak  2003-12-08 11:22:10  No: 52731  IP: [192.*.*.*]

>    int*    up = mCompP[1];                                // Cb
>    int*    vp = mCompP[2];                                // Cr
当然ながら、グレイスケールには「Cb、Cr」といった情報は必要ありません。
従って、グレイスケールJPEGには「Cb、Cr」の情報は保存されていません。
もし、グレイスケールJPEGなのに「Cb、Cr」の情報を取得するような処理を行えば、
以降のデータがズレる等の不具合が起こるかもしれません。

YCbCr から RGB への変換についてですが、グレイスケールの場合は単純に
    R = G = B = Y(輝度);
と RGB 各要素に Y(輝度) を設定してやるだけでOKです。

> 上記のソースでmHMax、mVMax を1に設定しても症状は変わりませんでした。
> デバッグしてみますと、RGBのハンドルエラーが出てしまいました。
> 下記のソースの中で*pBlue++に何も格納されていませんでした。(*pGreen++と*pRed++には格納されている。)
このエラーについてはソースを把握しているjpegさんにしか解決できないでしょう。

先にも言った通り、自分はソース全体を把握しているワケではありません。
よって、私の書いた事を反映するだけでは全てOKにはならないと思います。
先のレスで言いたかったのは
・グレイスケールの場合は 水平/垂直のサンプリングファクタ の値は無視
という点です。ちょっと書き方がまずかった…。

編集 削除
Junix  2003-12-08 15:11:07  No: 52732  IP: [192.*.*.*]

下記はデコードのソースですがエラーが回避できません。

/******************************************************
CJpegDecoder JPEGデコーダ・クラス
******************************************************/

#include <math.h>
#include <string.h>

#ifdef _MSC_VER            // Visual C++のバージョンが定義されていたら
#include "..\common\JPEG.h"      // Visual C++
#else
#include "JPEG.h"          // Code Warrior
#endif

#include "CInBitStream.h"
#include "CJpegDecoder.h"

// コンストラクタ
CJpegDecoder::CJpegDecoder( void )
{
  // ハフマンテーブル初期化
  for( int i=0; i<2; i++ )    // 0:DC成分, 1:AC成分
    for( int j=0; j<4; j++ )  // 4成分
    {
      mHT[i][j].SizeTP = mHT[i][j].CodeTP = mHT[i][j].ValueTP = nil;
      mHT[i][j].numOfElement = 0;
    }

  // 画像属性初期化
  mProperty.CanDecode = emYet;  // 未解析
  mProperty.Format = 0;      // フォーマット指定無し
  mProperty.MajorRevisions = 0;  // バージョン指定無し
  mProperty.MinorRevisions = 0;  // バージョン指定無し
  mProperty.Units = 0;      // 単位無し
  mProperty.HDensity = 1;      // 横密度1
  mProperty.VDensity = 1;      // 縦密度1
  mProperty.HThumbnail = 0;    // サムネイル横画素数0
  mProperty.VThumbnail = 0;    // サムネイル縦画素数0
  mProperty.ExtensionCode = 0;  // 拡張コード無し
  mProperty.SamplePrecision = 0;  // サンプル精度8ビット
  mProperty.Comment = nil;    // コメント無し

  mRestartInterval = 0;      // リスタートインタバル初期化:無効

  mIBSP = nil;          // ビットストリーム無し

  // cosテーブル作成
  for( int n=0; n<8; n++ )
    for( int m=0; m<8; m++ )
      mCosT[n][m] = cos(( 2 * m + 1) * n * kPaiDiv16);
}

// デストラクタ
CJpegDecoder::~CJpegDecoder( void )
{
  // テーブル廃棄
  for( int i=0; i<2; i++ )    // 0:DC成分, 1:AC成分
    for( int j=0; j<4; j++ )  // 4成分
    {
      if( mHT[i][j].CodeTP != nil )
        delete mHT[i][j].CodeTP;
      if( mHT[i][j].SizeTP != nil )
        delete mHT[i][j].SizeTP;
      if( mHT[i][j].ValueTP != nil )
        delete mHT[i][j].ValueTP;
    }

  // コメント領域廃棄
  if( mProperty.Comment != nil )
    delete mProperty.Comment;

  // ビットストリームの廃棄
  if( mIBSP != nil )
    delete mIBSP;
}

// JPEGデータを設定する
int
CJpegDecoder::SetJpegData(
  char*    aJpegDataP,      // JPEGデータ
  int      size )        // JPEGデータのサイズ
{
  // ビットストリームの生成
  if( mIBSP != nil )
    delete mIBSP;
  mIBSP = new CInBitStream( aJpegDataP, size );
  // ヘッダの解析
  return  DoAnalysis();      // ヘッダ解析結果を返す
}

// 変換後の画像データへのポインタを返す。
void
CJpegDecoder::GetPictData(
  u_char**  redP,        // 復号画像へのポインタ
  u_char**  greenP,        // 復号画像へのポインタ
  u_char**  blueP,        // 復号画像へのポインタ
  int&    HSize,        // 横画素数
  int&    VSize )        // 縦画素数
{
  HSize = mProperty.HSize;
  VSize = mProperty.VSize;

  *redP = mRgbP[0];
  *greenP = mRgbP[1];
  *blueP = mRgbP[2];
}

// 逆変換実行

#ifdef _MSC_VER            // Visual C++のバージョンが定義されていたら
#pragma warning( disable : 4101 )  // 未使用仮引数の警告を出さない
#endif

int
CJpegDecoder::DoDecode( void )
{
  try{
    // ハフマンテーブル,量子化テーブル,スキャンデータの存在の確認
    if( !(mProperty.CanDecode & (emIsHTable|emIsQTable|emIsStartData)) )
      return  emDataError;

    // 復号可能ならば
    Decode();
    return  emNoError;
  }

  catch( EMemoryError &me ){              // メモリエラー発生
    DeleteRGB();                  // RGB領域の廃棄
    return  emRuntimeError;              // 実行時エラー
  }

  catch( EIndexError &ie ){              // テーブルアクセス違反
    DeleteRGB();                  // RGB領域の廃棄
    return  emRuntimeError;              // 実行時エラー
  }

  catch( EBufferError &eob ){              // バッファアクセス違反
    DeleteRGB();                  // RGB領域の廃棄
    return  emRuntimeError;              // 実行時エラー
  }

  catch( EDataFormatError &dfe ){            // ハフマン符号復号失敗
    DeleteRGB();                  // RGB領域の廃棄
    return  emDataError;              // データエラー
  }

  catch( ENotSupported &ns ){              // 非対応フォーマット
    DeleteRGB();                  // RGB領域の廃棄
    return  emDataError;              // データエラー
  }
}

#ifdef _MSC_VER            // Visual C++のバージョンが定義されていたら
#pragma warning( default : 4101 )  // 警告設定を戻す
#endif

/******************************* protected ***********************************/

// ヘッダの解析

#ifdef _MSC_VER            // Visual C++のバージョンが定義されていたら
#pragma warning( disable : 4101 )  // 未使用仮引数の警告を出さない
#endif

int
CJpegDecoder::DoAnalysis( void )
{
  mEnable = 0;
  eMarker    mark;

  try{
    // 画像開始マーカを探す
    while( mEnable == 0 ){              // SOIマーカを探す
      mark = GetMarker();
      if( mark == emSOI )              // 画像開始
        mEnable = 1;
    }

    // 解析処理
    while( mEnable == 1 ){              // EOIマーカまで
      mProperty.CanDecode |= AnalysisMarker();  // 解析実行&結果の更新
      if( mProperty.CanDecode & emIsStartData )  // スキャンデータの始まり
        return  (int)emNoError;
    }

    return (int)emNoError;              // エラー無し(スキャンデータ無し)
  }  // end try

  // エラー処理
  catch( EMemoryError &me ){              // メモリエラー発生
    return (int)emRuntimeError;            // 実行時エラー
  }

  catch( EIndexError &ie ){              // テーブルアクセス違反
    return (int)emDataError;            // データ不正によるアクセス違反
  }

  catch( EBufferError &eob ){              // バッファアクセス違反
    return (int)emDataError;            // EOIマーカが含まれていない
  }

  catch( EDataFormatError &dfe ){            // ハフマン符号の復号失敗
    return  emDataError;              // データエラー
  }

  catch( ENotSupported &ns ){              // 非対応フォーマット
    return (int)emDataError;            // データ不正によるアクセス違反
  }
}

#ifdef _MSC_VER            // Visual C++のバージョンが定義されていたら
#pragma warning( default : 4101 )  // 警告設定を戻す
#endif

// マーカ解釈
int
CJpegDecoder::AnalysisMarker( void )          // 返値:解析結果
{
  int    length;
  eMarker  mark = GetMarker();

  switch( mark )
  {
    case emSOF0:  // フレーム処理(非差分ハフマン符号:基本DCT方式)
      length = mIBSP->GetWord() - 2;        // パラメータ長指定部分を引く
      AnalysisFrame();
      break;

    case emDHT:    // ハフマンテーブル
      length = mIBSP->GetWord() - 2;        // パラメータ長指定部分を引く
      AnalysisDHT( length );
      return emIsHTable;              // ハフマンテーブル有り

    case emDNL:    // ライン数定義
      length = mIBSP->GetWord() - 2;        // パラメータ長指定部分を引く
      mProperty.VSize = mIBSP->GetWord();      // ライン数
      break;

    case emDQT:    // 量子化テーブル定義
      length = mIBSP->GetWord() - 2;        // パラメータ長指定部分を引く
      AnalysisDQT( length );
      return emIsQTable;              // 量子化テーブル有り

    case emEOI:    // 画像終了
      mEnable = 0;
      break;

    case emSOS:    // スキャン開始
      length = mIBSP->GetWord() - 2;        // パラメータ長指定部分を引く
      AnalysisScan();
      return emIsStartData;            // スキャンデータ有り

    case emDRI:    // リスタートインタバル定義
      length = mIBSP->GetWord() - 2;        // パラメータ長指定部分を引く
      mRestartInterval = mIBSP->GetWord();
      break;

    case emCOM:    // コメント
      length = mIBSP->GetWord() - 2;        // パラメータ長指定部分を引く
      if( mProperty.Comment != nil )        // 最後のコメントのみ有効とする
        delete mProperty.Comment;
      mProperty.Comment = new( char[length + 1] );  // 念のため1バイト余計にとって'\0'をつける
      mIBSP->CopyByte( mProperty.Comment, length );
      *(mProperty.Comment+length) = '\0';      // 念のため
      return emIsComment;

    // 非対応フレーム処理
    case emSOF1:  // 非差分ハフマン符号:拡張シーケンシャルDCT方式
    case emSOF2:  // 非差分ハフマン符号:プログレッシブDCT方式
    case emSOF3:  // 非差分ハフマン符号:空間可逆方式
    case emSOF5:  // 差分ハフマン符号:差分シーケンシャルDCT方式
    case emSOF6:  // 差分ハフマン符号:差分プログレッシブDCT方式
    case emSOF7:  // 差分ハフマン符号:差分空間方式
    case emSOF9:  // 非差分算術符号:拡張シーケンシャルDCT方式
    case emSOF10:  // 非差分算術符号:プログレッシブDCT方式
    case emSOF11:  // 非差分算術符号:空間(シーケンシャル)可逆方式
    case emSOF13:  // 差分算術符号:差分シーケンシャルDCT方式
    case emSOF14:  // 差分算術符号:差分プログレッシブDCT方式
    case emSOF15:  // 差分算術符号:差分空間方式
    case emEXP:    // 参照成分拡大
    case emDAC:    // 算術符号化条件付け定義
    case emDHP:    // ハイアラーキカル・プログレッション定義
      throw( ENotSupported( mark, mIBSP->GetNextAddress() ) );
      break;

    // アプリケーションデータセグメント
    case emAPP0:  // JPEG FIF
      length = mIBSP->GetWord() - 2;        // パラメータ長指定部分を引く
      if( length >= 5 ){
        char  id[5];
        mIBSP->CopyByte( id, 5 );
        id[4] = '\0';              // 念のため
        if( strcmp( kJFIF, id ) == 0 ){      // JFIFフォーマットならば
          AnalysisJFIF();
          mIBSP->SkipByte( length - 14 );    // サムネイルはサポートしない
        }
        else if( strcmp( kJFXX, id ) == 0 ){  // JFXXフォーマットならば
          AnalysisJFXX();
          mIBSP->SkipByte( length - 1 );    // サムネイルはサポートしない
        }
        else                  // JFIFでもJFXXでもないならば
          mIBSP->SkipByte( length - 5 );    // このバージョンではサポートしないので読み飛ばす
      }
      else
        mIBSP->SkipByte( length );
      break;

    // 非対応マーカ
    case emAPP1:  case emAPP2:  case emAPP3:  case emAPP4:
    case emAPP5:  case emAPP6:  case emAPP7:  case emAPP8:
    case emAPP9:  case emAPP10:  case emAPP11:  case emAPP12:
    case emAPP13:  case emAPP14:  case emAPP15:
      length = mIBSP->GetWord() - 2;        // パラメータ長指定部分を引く
      mIBSP->SkipByte( length );          // このバージョンではサポートしないので読み飛ばす
      break;

    // リザーブ他
    case emJPG:    case emJPG0:  case emJPG1:  case emJPG2:
    case emJPG3:  case emJPG4:  case emJPG5:  case emJPG6:
    case emJPG7:  case emJPG8:  case emJPG9:  case emJPG10:
    case emJPG11:  case emJPG12:  case emJPG13:  case emTEM:
    // エラー
    case emError:
    default:
      throw( EDataFormatError( mIBSP->GetNextAddress()-1, "Marker error" ));
      break;
  }
  
  return  emYet;                    // 解析継続
}

// マーカ識別
// 読み出し位置をマーカの次にする
eMarker
CJpegDecoder::GetMarker( void )
{
  while( 1 )                      // バッファの終わりまで繰り返す
  {                          // バッファが終わるとCInBitStreamでthrowする
    u_char  c = mIBSP->GetByte();
    if( c == (u_char)emMarker ){          // 0xffのとき
      c = mIBSP->GetByte();            // 次のバイトを取り出し
      if( c != (u_char)0 )            // 0でなければ
        if( c > (u_char)emRESst && c < (u_char)emSOF0 )  // 定義されたマーカでないなら
          return  emError;          // エラー
        else                  // 定義されたマーカなら
          return (eMarker)c;          // マーカを返す
    }
  }
  return  emError;                  // dummy
}

// ハフマンテーブル解析
void
CJpegDecoder::AnalysisDHT(
  int      s )
{
  // 終了アドレス
  char*  theEndAddress = mIBSP->GetNextAddress();
  if( theEndAddress == nil )
    throw( EDataFormatError( theEndAddress, "AnalysisDHT:DHT Length" ) );
  theEndAddress += s;

  // 解析開始
  do{
    u_char  uc = mIBSP->GetByte();
    int    tc, th;
    if( (tc = uc>>4) > 1 )              // 0:DC成分, 1:AC成分
      throw( EDataFormatError( mIBSP->GetNextAddress()-1, "AnalysisDHT:Tc" ) );
    if( (th = uc & 0x0f) > 3 )            // 0〜3
      throw( EDataFormatError( mIBSP->GetNextAddress()-1, "AnalysisDHT:Th" ) );
    SHuffmanDecodeTable   &aHT = mHT[tc][th];    // 格納領域を指定

    int    i;                    // カウンタ
    int    num = 0;                // 符号語の数
    u_char  cc[16];
    for( i=0; i<16; i++ ){
      cc[i] = mIBSP->GetByte();
      num += cc[i];
    }
    aHT.numOfElement = num;              // 要素数

    // テーブル領域確保
    NewIntMemory( &aHT.SizeTP, num, 0, "AnalysisDHT:Huffman Size-T" );
    NewIntMemory( &aHT.CodeTP, num, 0, "AnalysisDHT:Huffman Code-T" );
    NewIntMemory( &aHT.ValueTP, num, 0, "AnalysisDHT:Huffman Value-T" );

    // サイズテーブル生成
    int    k = 0;                  // インデックス
    for( i=1; i<=16; i++ ){              // 符号語長iビットについて
      int  j=1;                  // SizeTP[]に記録した数が
      while( j <= cc[i-1] ){            // iビットの符号語数まで
        if( k >= num )              // 符号語総数がオーバーしたら
          throw( EIndexError( num, k, "AnalysisDHT:Size Table Index over" ) );  // アクセス違反監視
        aHT.SizeTP[k++] = i;          // 符号語長(ビット数)を記録
        j++;
      }
    }

    // 符号語テーブル生成
    k = 0;                      // インデックス
    int  code = 0;                  // 符号語(コード)
    int  si = aHT.SizeTP[0];              // サイズ(ビット数)
    while( 1 ){
      while( aHT.SizeTP[k] == si )        // サイズの確認
        aHT.CodeTP[k++] = code++;        // 符号語の決定
      if( k >= num )                // 終了判定
        break;                  // 終了

      do {                    // 次のサイズの準備
        code <<= 1;                // 符号語長を1ビット増やす
        si++;                  // サイズを1ビット増やす
      } while( aHT.SizeTP[k] != si );        // siビットの符号語がないとき繰り返し
    }  // while(1)

    // 復号テーブル生成
    for( k=0; k<num; k++ )
      aHT.ValueTP[k] = mIBSP->GetByte();      // 対応する値の取り出し

  }while( mIBSP->GetNextAddress() < theEndAddress );
}

// 量子化テーブル解析
void
CJpegDecoder::AnalysisDQT(
  int      s )
{
  char* theEndAddress = mIBSP->GetNextAddress() + s;  // 終了アドレス

  do {
    // テーブルの指定
    u_char  c = mIBSP->GetByte();
    int    *aQT = mQT[ c&0x3 ];          // テーブルは最大4つ

    int    Pq = c >> 4;              // エレメント精度 0:要素Qkは8ビット, 1:16ビット
    if( Pq == 0 )                  // 8ビット要素
      for( int i=0; i<64; i++ )
        aQT[kZigzag[i]] = int( u_char( mIBSP->GetByte() ) );
    else                      // 16ビット要素
      for( int i=0; i<64; i++ )
        aQT[kZigzag[i]] = mIBSP->GetWord();

  }while( mIBSP->GetNextAddress() < theEndAddress );
}

// フレームヘッダ解析
void
CJpegDecoder::AnalysisFrame( void )
{
  mProperty.SamplePrecision = mIBSP->GetByte();    // サンプル精度
  mProperty.VSize = mIBSP->GetWord();          // 画像横サイズ
  mProperty.HSize = mIBSP->GetWord();          // 画像縦サイズ
  mProperty.Dimension = mIBSP->GetByte();        // 成分数
  if( mProperty.Dimension != 3 && mProperty.Dimension != 1 )  // 成分数が3または1でない場合
    throw( ENotSupported( emSOF0, mIBSP->GetNextAddress() ) );  // サポートしない

  int i;
  //mHMax = mVMax = 0;
  mHMax = mVMax = 1;
  for( i=0; i<mProperty.Dimension; i++ ){
    mFComp[i].C = mIBSP->GetByte();          // 成分識別子
    u_char  c = mIBSP->GetByte();

    mFComp[i].H = c >> 4;              // 水平サンプリングファクタ
    if( mFComp[i].H > mHMax )
      mHMax = mFComp[i].H;

    mFComp[i].V = c & 0xf;              // 垂直サンプリングファクタ
    if( mFComp[i].V > mVMax )
      mVMax = mFComp[i].V;

    mFComp[i].Tq = mIBSP->GetByte();        // 量子化テーブルセレクタ
  }
}

// スキャンヘッダ解析
void
CJpegDecoder::AnalysisScan( void )
{
  u_char  c;
  mSHeader.numOfScanComp = mIBSP->GetByte();      // スキャン成分数

  for( int i=0; i<mSHeader.numOfScanComp; i++ ){
    mSComp[i].Cs = mIBSP->GetByte();        // スキャン成分セレクタ
    c = mIBSP->GetByte();
    mSComp[i].Td = c >> 4;              // DC成分用ハフマンテーブルのセレクタ
    if( mSComp[i].Td > 2 )
      throw( EIndexError( 2, mSComp[i].Td, "AnalysisScan:Td Over" ) );
    mSComp[i].Ta = c & 0xf;              // AC成分用ハフマンテーブルのセレクタ
    if( mSComp[i].Ta > 2 )
      throw( EIndexError( 2, mSComp[i].Ta, "AnalysisScan:Ta Over" ) );
  }

  mSHeader.SpectralStart = mIBSP->GetByte();      // 未使用
  mSHeader.SpectralEnd = mIBSP->GetByte();      // 未使用
  c = mIBSP->GetByte();                // 未使用
  mSHeader.Ah = c >> 4;                // 未使用
  mSHeader.Al = c & 0xf;                // 未使用
}

// JFIFフォーマット解析
void
CJpegDecoder::AnalysisJFIF( void )
{
  mProperty.Format = 1;                // JFIF
  mProperty.MajorRevisions = mIBSP->GetByte();    // バージョン上位
  mProperty.MinorRevisions = mIBSP->GetByte();    // バージョン下位
  mProperty.Units = mIBSP->GetByte();          // 単位 0:無し、1:dots/inch、2:dots/cm
  mProperty.HDensity = mIBSP->GetWord();        // 横密度
  mProperty.VDensity = mIBSP->GetWord();        // 縦密度
  mProperty.HThumbnail = mIBSP->GetByte();      // サムネイル横画素数
  mProperty.VThumbnail = mIBSP->GetByte();      // サムネイル縦画素数
  mProperty.CanDecode |= emIsJFIF;          // JFIFフォーマット指定有り
}

// JFXXフォーマット解析
void
CJpegDecoder::AnalysisJFXX( void )
{
  mProperty.Format = 2;                // JFXX
  mProperty.ExtensionCode = mIBSP->GetByte();      // 拡張コード 0x10:JPEG, 0x11:1byte/pixel, 0x13:3bytes/pixel
}

// JPEG逆変換実行
void
CJpegDecoder::Decode( void )
{
  mHMax = mVMax = 1;mHMax = mVMax = 1;
  // 定数の計算
  int    VBlocks = GetBlocks(mProperty.VSize);    // 縦ブロック数
  int    HBlocks = GetBlocks(mProperty.HSize);    // 横ブロック数
  int    numHUnit = HBlocks / mHMax;          // 横ユニット数
  if( HBlocks % mHMax )
    numHUnit++;
  int    numVUnit = VBlocks / mVMax;          // 縦ユニット数
  if( VBlocks % mVMax )
    numVUnit++;

  // 結果の格納領域を確保
  int    rgbSize = (numVUnit * mVMax) * 8 * (numHUnit * mHMax) * 8;  // 符号に含まれる付加部分も含んでいる

  mRgbP[0] = new u_char[ rgbSize ];          // 赤成分
  if( mRgbP[0] == nil )
    throw( EMemoryError( rgbSize, "Decode:mRgbP[0]" ));
  mRgbP[1] = new u_char[ rgbSize ];          // 緑成分
  if( mRgbP[1] == nil )
    throw( EMemoryError( rgbSize, "Decode:mRgbP[1]" ));
  mRgbP[2] = new u_char[ rgbSize ];          // 青成分
  if( mRgbP[2] == nil )
    throw( EMemoryError( rgbSize, "Decode:mRgbP[2]" ));

  // 結果の格納領域の確保
  mCompP[0] = mCompP[1] = mCompP[2] = nil;
  int  unitSize = mHMax * mVMax * 64;          // ユニット内データサイズ
  NewIntMemory( &mCompP[0], unitSize, 254, "Disconvert unit1" );
  NewIntMemory( &mCompP[1], unitSize, 0x80, "Disconvert unit2" );
  NewIntMemory( &mCompP[2], unitSize, 0x80, "Disconvert unit3" );

  mPreDC[0] = mPreDC[1] = mPreDC[2] = 0;        // 前ブロックのDC成分値を初期化
  int    restartCount = 0;              // インタバルカウンタ
    mRestartInterval = 0;    
  // ユニットごとに処理
  for( int uy=0; uy<numVUnit; uy++ ){          // 縦ユニット指数
    for( int ux=0; ux<numHUnit; ux++ ){        // 横ユニット指数

      DecodeMCU();                // MCUの処理
      makeRGB( ux, uy );              // RGBに変換

      if( mRestartInterval ){            // リスタートインタバル有効ならば
        if(++restartCount >= mRestartInterval){  // インタバル終了ならば
          restartCount = 0;          // カウンタリセット
          eMarker    mark = GetMarker();    // マーカ検出
          if( mark >= emRST0 && mark <= emRST7 )  // リスタートマーカならば
            mPreDC[0] = mPreDC[1] = mPreDC[2] = 0;  // 前ブロックのDC成分値をリセット
        }  // if( ++restartCount >= ・・・
      }  // if( mRestartInterval )
    }  // for ux
  }  // for uy

  delete mCompP[0];  delete mCompP[1];  //delete mCompP[1];
}

// MCUデコード
void
CJpegDecoder::DecodeMCU( void )
{
  // 成分ごとに
  for( int sc=0; sc<mProperty.Dimension; sc++ ){
    int  numV = mFComp[sc].V;            // 垂直サンプリングファクタ
    int  numH = mFComp[sc].H;            // 水平サンプリングファクタ
    int  cntY = mVMax/numV;              // ユニット内重複数
    int  cntX = mHMax/numH;              // ユニット内重複数
    int  vStep = mHMax * 8;              // 縦方向のアドレス増加量

    for( int ky=0; ky<numV; ky++ ){          // ユニット内縦ブロックごと
      for( int kx=0; kx<numH; kx++ ){        // ユニット内横ブロックごと
        DecodeHuffmanBlock( sc );        // エントロピー符号の復号
        InverseQuantization( sc );        // 逆量子化
        InverseDCT();              // 逆DCT変換

        // ブロック単位のデータを表示順序に変換&間引かれたものを元に戻す
        int*  tp = mCompP[sc] + ky * vStep * 8 + kx * 8;
        for( int yInU=0; yInU<8*cntY; yInU++ )
          for( int xInU=0; xInU<8*cntX; xInU++ )
            tp[ yInU * vStep + xInU ] = mBlockP[(yInU / cntY) * 8 + (xInU / cntX)];
      }  // kx
    }  // ky
  }  // sc
}

// RGB変換
void
CJpegDecoder::makeRGB(
  int      ux,                    // 横ユニット位置
  int      uy )                  // 縦ユニット位置
{
  mHMax = mVMax = 1;
  int*  yp = mCompP[0];                // Y
  int*  up = mCompP[1];                // Cb
  int*  vp = mCompP[2];                // Cr
  //グレースケールの場合、Cb = Cr = 0;
  int    numLine = uy * mVMax * 8;          // ライン:縦ユニット数×ユニットライン数
  int    offsetV = numLine * (mProperty.HSize);    // 縦オフセット:ライン×1ラインドット数
  int    offsetH = ux * mHMax * 8;          // 横オフセット:横ユニット数×ユニット内横ブロック数×ブロック内ドット数
  int    offset = offsetV + offsetH;          // 総合オフセット
  u_char*  rp = mRgbP[0] + offset;            // 赤書き込み位置
  u_char*  gp = mRgbP[1] + offset;            // 緑書き込み位置
  u_char*  bp = mRgbP[2] + offset;            // 青書き込み位置
  int    endX = mHMax*8;                // 横ループ終了ポイント
  int    endY = mVMax*8;                // 縦ループ終了ポイント

  for( int picY=0; picY<endY; picY++ ){
    for( int picX=0; picX<endX; picX++ ){
      if( picX + offsetH >= mProperty.HSize/3 ){  // 付加ビットならば
        yp += endX - picX;            // ソースアドレスを更新
        up += endX - picX;
        vp += endX - picX;
        break;                  // 次のラインへ
      }

      int  index = picY * mProperty.HSize+ picX;  // 書き込み位置

#ifdef GRAY
    //  gp[index] = bp[index] =  rp[index] = ReviseValue( *yp++ );
#else
  double  v = *yp              + (*vp - 0x80) * 1.4020;
      rp[index] = ReviseValue( v );        // R
      v = *yp - (*up - 0x80) * 0.3441  - (*vp - 0x80) * 0.7139;
      gp[index] = ReviseValue( v );        // G
      v = *yp + (*up - 0x80) * 1.7718;
      bp[index] = ReviseValue( v );        // B

      yp++; vp++; up++;
#endif
    }  // picX
  }  // picY
}

// 1ブロック分のエントロピー符号を復号(逆量子化を含む)
void
CJpegDecoder::DecodeHuffmanBlock(
  int    sc )                    // 成分番号
{
// DC成分復号
  int    diff = 0;                  // DC成分差分
  // ハフマン符号1語の復号
  int category = Decode1HuffmanCode( 0, sc );      // カテゴリ(差分値ビット数)
  if( category > 0 ){
    diff = mIBSP->GetBits( category );
    if( ( diff & ( 1 << (category-1) )) == 0 )    // 差分が負
      diff -= (1 << category ) - 1;
  }
  else if( category < 0 )                // マーカ
    throw( EDataFormatError( mIBSP->GetNextAddress(), "DecodeHuffman:Error Marker" ) );

  mPreDC[sc] += diff;
  mDctDataP[0] = mPreDC[sc];              // DC成分値設定(並び替え不要)

// AC成分復号

  int   k = 1;
  while( k < 64 ){
    // ハフマン符号1語の復号
    category = Decode1HuffmanCode( 1, sc );      // ランレングスとカテゴリ
    if( category == 0 ){              // EOB
      while( k < 64 )
        mDctDataP[kZigzag[k++]] = 0;
      break;
    }
    else if( category < 0 )             // マーカ
      throw( EDataFormatError( mIBSP->GetNextAddress(), "DecodeHuffman:Error Marker" ) );

    int  run = category >> 4;            // ランレングス
    category &= 0x0f;                // カテゴリ

    int  acv = 0;                  // AC成分値
    if( category ){
      acv = mIBSP->GetBits( category );      // AC成分取り出し
      if( (acv & ( 1 << (category-1) )) == 0 )  // 成分が負
        acv -= (1 << category ) - 1;
    }
    else if( run != 15 )              // EOBでもZRLでない
      throw( EDataFormatError( mIBSP->GetNextAddress(), "DecodeHuffman:wrong Huffman value" ) );

    if( run + k > 63 )                // 係数が多すぎる
      throw( EDataFormatError( mIBSP->GetNextAddress(), "DecodeHuffman:wrong Run length" ) );

    while( run-- > 0 )                // ランレングスの数だけ0
      mDctDataP[kZigzag[k++]] = 0;

    mDctDataP[kZigzag[k++]] = acv;          // 順序変換

  }
}

// ハフマン符号1語の復号
int
CJpegDecoder::Decode1HuffmanCode(
  int    tc,                      // テーブルクラス 0:DC成分, 1:AC成分
  int    sc )                    // 成分番号
{
  // ハフマンテーブル指定
  SHuffmanDecodeTable  &theHT = mHT[tc][mSComp[sc].Td];  // 使用するハフマンテーブル

  int    code = 0;                  // ハフマン符号語の候補:最大値16ビット
  int    length = 0;                  // ハフマン符号語候補のビット数
  int    next = 0;                  // 次の1ビット
  int    k = 0;                    // 表の指数

  while( k < theHT.numOfElement && length < 16 )
  {
    length++;
    code <<= 1;
    next = mIBSP->GetBits( 1 );
    if( next < 0 )                  // マーカだったら
      return  next;
    code |= next;

    while( theHT.SizeTP[k] == length ){        // 候補と符号語のビット数が等しい間だ検索
      if( theHT.CodeTP[k] == code )        // ヒット
        return theHT.ValueTP[k];        // 復号結果を返す
      k++;                    // 次の符号語
    }
  }

  throw( EDataFormatError( mIBSP->GetNextAddress(), "Decode1HuffmanCode:length over" ) );
  return  (u_char)0;                  // Visual C++用ダミー
}

// 逆量子化
void
CJpegDecoder::InverseQuantization( 
  int    sc )                      // 成分番号
{
  int  *p = mDctDataP;                    // DCT係数データ
  int  *aQT = mQT[mFComp[sc].Tq];              // 量子化テーブル

  for( int i=0; i<64; i++ )
    *p++ *= *aQT++;                    // 逆量子化
}

// 逆DCT変換
void
CJpegDecoder::InverseDCT( void )
{
  int  sl = mProperty.SamplePrecision == 8 ? 128 : 2048;  // レベルシフト値の決定

  for( int y=0; y<8; y++ ){
    for( int x=0; x<8; x++ ){
      double  sum = 0;

      for( int v=0; v<8; v++ ){
        double cv = v==0 ? kDisSqrt2 : 1.0;
        for( int u=0; u<8; u++ ){
          double cu = u==0 ? kDisSqrt2 : 1.0;

          sum += cu * cv * mDctDataP[ v * 8 + u ] * mCosT[u][x] * mCosT[v][y];
        }
      }
      mBlockP[ y*8+x ] = int( sum / 4 + sl );
    }
  }
}


// RGB算出用値補正
u_char
CJpegDecoder::ReviseValue(                // 返値:補正値
  double  v )                      // 値
{
  if( v < 0.0 )
    return 0;
  if( v > 255.0 )
    return 255;
  return (u_char)v;
}

// エラー時のRGBデータ領域の廃棄
void
CJpegDecoder::DeleteRGB( void )
{
  if( mRgbP[0] != nil )  delete mRgbP[0];
  if( mRgbP[1] != nil )  delete mRgbP[1];
  if( mRgbP[2] != nil )  delete mRgbP[2];
}

// intサイズの領域確保
void
CJpegDecoder::NewIntMemory(
  int**  p,      // ポインタ
  int    s,      // サイズ
  int    v,      // 初期値
  char  *mes )    // エラーメッセージ
{
  if( *p != nil )
    delete *p;
  *p = new int[s];
  if( *p == nil )
    throw( EMemoryError( s, mes ) );
  while( s-- )
    (*p)[s] = v;
}

編集 削除
Gak  2003-12-08 18:04:01  No: 52733  IP: [192.*.*.*]

今更だけど
  #ifdef GRAY
    ↓
  #if 1
単純にこれだけじゃダメだった?

編集 削除
junix  2003-12-08 18:31:21  No: 52734  IP: [192.*.*.*]

だめでした。上記のソースの内、MakeRGBとDecodeMCUが問題ですが、色々やってはみましたがうまくいきません。

編集 削除
匿名X  2003-12-09 16:26:16  No: 52735  IP: [192.*.*.*]

for文のところどころで  {  が無いところあるんですけど・・・
ちなみに、ま、人それぞれかもしれませんが、if文とかfor文とか
一行なら  {}を省略できますけど、省略しないほうがいいかと。

編集 削除