なるので、 New ェ n む Mem 。 ry ( ) という非公開メソッドを用意しています。得られた領域の アドレスは、 aHT の各要素に格納します。以後の作業で、これらの領域に具体的な値を設 定していきます。 ・サイズテーブルの作成 ハフマン符号を復号するとき、ビットストリームから符号語の候補を取り出して、一致 する符号語があるかどうかを確認します。このとき、 SHuffmanDecodeTab1e. COdeTP の要素と比較するのですが、その対象は符号語の候補と同じビット数の符号語に限ります。 そのため、各符号語が何ビットであるかを示す、サイズテープルが必要です。サイズテ 土 n し k = 0 ー / / インデックス / / サイズテープル生成 ープルは以下の手順で作成します。 / / 符号語総数がオーバーしたら / / 土ビットの符号語数まで SizeTP [ ] に記録した数が / / 符号語長土ビットについて / / 語長 ( ビット数 ) を記録 "AnalysisDHT:Size Tab1e 工 ndex over" k ′ if ( k > = num ) whi 1 e ( j < = c c は一 1 ] ) { int j=l; for( 土 = 1 ー i く = 16 ー土 + + ) { aHT . SizeTP[k + + ] EIndexError ( num, throw ( / / アクセス違反監視 とピットストリームから得られた符号語の候補を比較して、一致した符号語を探すのです。 次は、具体的なハフマン符号語の一覧表を作成します。ハフマン復号処理では、符号語 ・符号語テーブルの作成 います。速度を少しでも改善したいときはポインタ演算に置き換えてください。 このとき、 aHT . s 土 zeTP はポインタですが、添字を使うことで要素を特定しやすくして aHT. s 土 zeTP には先頭から順に 3 , 3 , 3 , 5 , 5 の 5 つのデータが格納されます。 たとえば 1 , 2 , 3 , 4 , 5 ビットの符号語がそれぞれ 0 , 0 , 3 , 0 , 2 個すつあった場合、 イズテープルを先頭から順に、 cc は一 1 ] 個すっ土を格納します。 復号用ハフマンテープルは、符号語のビット数が少ない順に並べて作ります。そこで、サ 配列 c c [ ] には、土ビットの符号語が何個あるかという情報が入っています。そして、 / / 符号語テープル生成 k = 0; 土 n し code 土 n し Si wh 土 1 e ( 1 ) { wh 土 1 e ( aHT . S 土 z eT p [ k ] aHT . CodeTP [ k + + ] if ( k > = num ) break; aHT . S 土 z eT P [ 0 ] ー s 土 ) COde 十十一 インデックス / / 符号語 ( コード ) / / サイズ ( ビット数 ) / / サイズの確認 / / 符号語の決定 / / 終了判定 / / 終了 4.5 CJpegDecoder クラス
として、 DQT セグメントのパラメータをジグザグシーケンスではなく、メモリ順にして 出力したものです。 このように、画質に関する間題は量子化テープルに関わることが多いです。 Fig. 5.15 DQT セグメントの作成誤りによる復号時の画質の劣化 住 3 ・画像がめちゃくちゃ Fig. 5.16 は、 WriteHeader ( ) で mOBSP->SetByte( i==O ? O ープル指定 AC ノ、フマンテ 0 x 11 ) : ープル指定 AC ノ、フマンテ mOBSP->SetByte( i==0 ? Oxll と置き換えて、符号化と復号で異なるハフマンテープルを使用するようにして作ったもの です。 Fig. 5.16 DHT セグメントの作成誤りによる復号時の画質の劣化 1 当 ll 207 5.4 CJpegCoder のテスト
ー JPEG プログラムの現状と本書の目的 インターネットと CD - ROM の普及によって、美しい静止画像を小さなデータで提供す る最も有効な手段として JPEG が浸透しました。インターネットェクスプローラやネット スケーブナビゲータなどの WWW プラウザは、必ず JPEG 画像を表示する機能を備えてい ます。もはや、 JPEG 画像を表示することができないパソコンシステムは珍しくなってい ます。 ユーザだけでなく、プログラマにとっても JPEG は身近なものになっています。 Windows でも Macintosh でも、 JPEG の符号化・復号 ( 圧縮・伸張 ) プログラムはコンポ ーネント ( ソフトウェア部品・ライプラリ ) として提供されているものがあり、もはや JPEG のプログラムなど作成する必要はないという人までいます。 はたして本当にそうでしようか ? 既成のコンポーネントを利用してシステムを構築する方法は、システム開発の期間の短 縮と経費の節約に大きな効果があります。そして、そのような方法はソフトウェア工学に おける重要なテーマです。 ところが、そのような方法は、実際には問題をすり替えているに過ぎないという意見も あります。コンポーネント部分のプログラムを作成しなくてよい反面、その部分には一切 手を出せなくなってしまったのです。既成のコンポーネントを利用することで通常の符号 化・復号はできても、 JPEG を応用した特別なシステムの開発はできません。しよせん、 既成の範囲から抜け出すことはできないのです。 また、ほとんどのプログラマが既成のコンポーネントを使用するので、 JPEG に詳しい 人が減り ( 増えす ) 、 JPEG が「特別な技術」という扱いを受けることになりつつあります。 実際には、 JPEG が特殊技術という扱いを受ける理山は何もありません。むしろ、これだ け普及しているのですから、すべてのプログラマがその基礎技術に精通していても不思議 ではないのです。 一方、プログラミング言語を覚えたばかりの、初級から中級へ移ろうとしているプログ ラマにとっても、 JPEG は魅力的なテーマです。 JPEG プログラムを作成することによって、 プログラミングの様々な概念と技術を得ることができるでしよう。また、 JPEG について しく知っておくことは、決して損にはなりません。 ところで、「それでは JPEG プログラムを作成してみよう ! 」と思って準備を始めると、 情報の少なさに驚かされます。インターネットで JPEG について検索してみてください。 ほとんどの情報は、 JPEG データの特徴とか、 DCT 変換とハフマン符号 ( 後述 ) を使う、 という内容に限られています。このことは、いわゆる JPEG の解説書でも同じです。 では、その情報だけでプログラムが作成できるのか ? というと、答えは「作成できる のは半分だけ」となります。 JPEG に似た独自の圧縮・伸張プログラムを作ることはでき ますが、他のプログラムとのデータ交換はとても無理です。 DCT 変換とハフマン符号を 扱う部分が CPU だとすると、レ () にあたる部分の情報が完全に欠落しているのです。 第 1 章本書の概要 二一一口
ルで行っています。 3.5.1 ■ 11 ・概要 COutBitStream クラス JPEG 符号化を行うときは、作成中の JPEG データをどこに保持するかということを決 めておかなければなりません。選択肢はいくつかありますが、本書ではメモリ上にバッフ アを作成することにしました。そして、そのバッフアをオプジェクトとし、 1 ピットから 数バイトまでの様々な単位のアクセスを受け付け、管理させることにしました。 このバッフアは一方通行です。つまり、書き込みたいデータを追加していくだけです。 戻って読み直したり、書き直したりすることはありません。 ビット単位の書き込みを行うときは、 JPEG の次の 2 つの決まりに従います。 ・ビット列とバイト列の間にある余ったビットは 1 にセットする ・バイト単位の区切りで見たときに、マーカでない 0xFF のビット列ができたときは 次のバイトを 0X00 にする データが完成したら、バッフアの管理を他のオプジェクトに任せます。ファイルへの出 ログラムの可読性をよくするために作ることにします。 1 バイト書き込みを繰り返せば、この 2 つの機能は必要ないのですが、能率とソースプ ・ n バイトのデータを書き込む ・ 2 バイトのデータを書き込む また、あると便利な機能が次の 2 つです。 ・バッフアの有効サイズを渡す ・バッフアの先頭アドレスを渡す ~ 1 6 ビットのデータを書き込む ・ 1 バイトのデータを書き込む ・バッフアを作成する C0utBitstream が実現するべき機能は以下のとおりです。 0 】 2 ・機能 以上の機能を cou し B s し rea Ⅲで実現します。 力などには関わりません。 64 第 3 章仕様と論理設計
ドキュメントクラスは、 CJpegCodWi nDo c です。 BMP ファイルのオープンと表示、 JPEG 符号化とファイル保存の制御を行います。 BMP ファイルならばすべて開くことがで きますが、 JPEG 符号化できるのは 16 色以上のデータだけです。 16 色未満のデータは表示 はできますが、 JPEG 符号化はできません。 オープンした BMP 画像の表示を行うのが CJpegC 。 dW 土 nv 土 ew です。このクラスは CScr011V 土 ew からの派生クラスです。 BMP データから DIB (RGB) データを作るために CDib というクラスを作成しました。 その他に CJpegCoder と coutBitStream が含まれています。 このプロジェクトは CJpegC 。 der と c 〇 u に B 土し s し ream の動作を確認するためのもので す。ですから、 BMP ファイルのオープンと表示、符号化して得られた JPEG データのファ イル保存機能しかあり . ません。実用的なアプリケーションは読者が独自に作成してください。 ・ファイルの読み出し メニューから [ ファイル ] - [ 開く ] を選ぶと、ファイル選択ダイアログが開きます。 ファイルを選択すると、 CJpegCodWinDoc: :OnOpenDocument( ) が呼び出されます。 こで BMP ファイルを読み込み、 DIB データを作成しています。 / / CJpegC0dWinDoc コマンド BOOL CJpegCodWinDoc : : OnOpenDocument (LPCTSTR 1pszPathName ) if ( ! CDocument : : OnOpenDocument ( 1pszPathName ) ) return FALSE; / / TODO : この位置に固有の作成用コードを追加してください try{ CFi1e BmpFi1e (1pszPathName, CFi1e: :modeRead) : m Dib . BmpLoad(BmpFi1e) : BmpFi1e . C10se() : }catch (CDibException eDib) { CString strMessage; 1pszPathName : strMessage strMessage + = strMessage + = eDib . GetErrMessage( ) ー AfxMessageBox(strMessage) 冫 return FALSE; }catch (CException e) { TCHAR szErrBuf [ 256 ] ー e . GetErrorMessage(szErrBuf, AfxMessageBox ( szErrBuf ) : return FALSE : return TRUE ー DIB データができれば、 CJpegCodWinView : s 土 z e 0 f ( s z ErrBu f ) ) : :0nDraw( ) で表示されます。 90 第 4 章実装
ファイル名 ファイルは、クラスごとに作成します。クラスの宣言は、ヘッダファイル " クラス名 . h " で行います。メソッドは、ソースファイル " クラス名 . cpp" に記述します。 CJpegC0der. h, CJpegC0der. CPP 例 CJpegC0der → 他にも、必要に応じて適宜ヘッダファイルを作成します。そのファイル名は一見して意 味のわかるものにします。 3 ゴ 2 ー 型 規約ではありませんが、プログラム中でよく使う型名にし ypedef 宣言を用いて、短い 同義語を定義しています。たいへん便利ですので、これ以降、本文中でも使用します。 typedef unsigned char u—char : typedef unsigned 土 n に u—int 冫 32 ー全体の構成 本書では主に 2 つのクラスを作成します。 1 つは RGB 表色系で構成された画像を JPEG 形式に符号化するクラス、 CJpegCoder です。もう 1 つは、 JPEG 形式のデータを復号し て RGB 表色系の画像データを作るクラス、 CJpegDecoder です。 JPEG データの読み書きでは、バイト単位でなくビット単位で行わなければならないこ とがあります。この操作は、 c + + の入出力機能や各種 ()S のシステムコールだけでは不十 分です。そこで、ビット単位の書き込みを行うクラス c 〇 u し B s し rea Ⅲと、ビット単位の 読み出しを行うクラス c 工 nB s し ream を作成します。 CJpegcoder, CJpegDecoder を生成して呼び出すクラス、およびアプリケーション 全体の構成については、本書では詳しくは説明しません。付録 CD-ROM に、 Windows 用 の Visual C + + 5.0 と MFC4.2 を用いたプロジェクトファイル群と、 Macintosh 用の CodeWarroirPr03 と PowerPIant 1.9 を用いたプロジェクトファイル群を添付しました。 これらのプロジェクトを調べてみてください。それぞれのプラットフォームとフレーム ワークでのアプリケーション開発の経験が十分にあれば、アプリケーションで JPEG デー タを扱う方法が簡単にわかるでしよう。 第 2 章でも触れたとおり、エラー処理はとても重要です。プログラムのなかでは、多く の場所でエラーが発生する可能性があります。しかし、 C + + の例外処理を用いれば、そ れらのエラーに一括して対処することができます。つまり、少数の catch ハンドラを効 果的な位置に設けることで、エラー処理をソースプログラムの一部に集中させることがで 3.2 全体の構成
iJpegSize; Ⅲ bCodeF1g = TRUE; delete rp; delete gp; delete bp; ますはじめに、 JPEG データの属性を設定します。画像サイズは BMP データのとおりで、 成分数は 3 に固定します。コメントは適当なものを設定しています。画像密度は 72dpi と しています。 次に、 DivideBitData( ) で RGB データを作成します。 こまでできたら、いよいよ符号化を実行します。ます、 CJpegcoder のインスタンス を生成し、 RGB データのアドレスを渡します。次に、 SetJpegProperty() で JPEG の画 像属性をセットします。そして、 D 。 c 。 d 土 ng ( ) で符号化を実行します。最後に、データを 取り出します。 Docoding() では、エラーが発生するかもしれません。工ラーの場合、例外を送出して ダイアログを表示します。 0 コ 2 ・ JPEG 復号アプリケーション ・プロジェクトの構成 JPEG 復号プロジェクトワークスペースは "JpegDecodeWin. dsw" という名称です。ワー クスペースの FileView を Fig. 4.2 に示します。 プロジェクトの種類は "MFC AppWizard(exe)" の "MDI" です。印刷や保存はサポート しません。 アプリケーションクラスは CJpegDecodeWinApp です。 ドキュメントクラスは CJpegDecodeWinDoc です。復号する JPEG ファイルのオープ ンと復号・表示の制御を行います。 復号した画像の表示を行うのが CJpegDecodeWinView です。このクラスは cscr 。 11 v 土 ew からの派生クラスです。 RGB データから DIB データを作るために、 CD 土 bExcep 。 n というクラスを作成しま 0 その他に CJpegDecoder と c 工 nBitstream が含まれています。 このプロジェクトは、 CJpegDecoder と c 工 nBitStream の動作を確認するためのもの です。ですから、 JPEG ファイルのオープンと復号された画像の表示機能しかありません。 実用的なアプリケーションは読者が独自に作成してください。 ・ファイルの読み出し メニューから [ ファイル ] - [ 開く ] を選ぶと、ファイル選択ダイアログが開きます。 ファイルを選択すると、 CJpegDecodeWinDoc : :on0penDocument( ) が呼び出されます。 こで、 L 〇 adJpegF 土 le ( ) を呼び出します。ファイルの読み込みはこのメソッドの前半 4.1 上位クラス で行っています。
JPEG における デジタル静止画像圧縮 本章では、 JPEG そのものの説明を行います。 2.1 節では、画像処理に精通されていない方のために、カラー画像の扱い方を 簡単に説明します。理解されている方は読み飛ばしてもかまいません。 2.2 節では、 JPEG とはどんなものかというイメージを作ってみましよう。 2.3 節では、 JPEG の種類を紹介します。 JPEG は複数の方式の組で、一般に利 用されているのはその一部の機能とフォーマットだけです。 2.4 節では、最もよく利用されている基本 DCT 方式の原理を説明します。本書 は、基本 DCT 方式についてのみ詳しく説明します。 2.5 節では、 JPEG に採用されている画像伝送技術を説明します。この技術はコ ンピュータにおける画像処理よりは、テレビ放送などの技術に近いものです。 の技術の概念はさほど難しくありませんが、実現には手間がかかります。しつか りと理解しておかないと、実際にプログラムを作成することができません。 2.6 節では、基本 DCT 方式で利用する JPEG データのフォーマットを詳説しま す。プログラムの作成には欠かせないものです。 2.7 節、 2.8 節では、それぞれ符号化と復号の処理の流れを説明します。これは、 そのままプログラムのアルゴリズムの概要になります。 2.9 節では、 JPEG とは異なった JFIF という規則について説明します。これは JPEG の利用方法を定めた規則です。インターネットなどで見かける JPEG デー タはすべて JFIF に従っていると考えてよいでしよう。 最後に、 2.10 節では、 JPEG アプリケーションを C + + で実現する利点について 説明します。他の言語で実現する場合にも参考になるでしよう。
return TRUE; ・画面表示 描画では、必要に応じて、随時フレームワークから各ビューの OnDraw ( ) が呼び出され ることで行われます。 / / CJpegDecodeWinView クラスの描画 ASSERT_VAL 工 D (pDoc) ー CJpegDecodeWinDoc* pDoc ニ GetDocument ( ) : VOid CJpegDecodeWinView: : OnDraw (CDC* pDC) AfxMessageBox ( eDib . GetErrMessage ( ) ) ー }catch(CDibException eDib) { pDoc—>GetDib ( ) —>Draw 工 mage (pDC ′ Pos ) ー try{ / / 復号したこ PEG データを表示 CPoint Pos(0,O) : return,• / / プリンタ出力はサポートしない if(pDC-> 工 sPrinting() ) { / / TODO : この場所にネイテイプデータ用の描画コードを追加します Macintosh coder と CJpegDecoder の使い方を参考にして、独自にアプリケーションを作成してみ これ以外の開発環境を使用している方は、本書付属のサンプルプログラム中の c こ peg けてあります。 部分はプロジェクト生成時に作られたコードです。変更部分には、日本語のコメントがっ ドキュメントを参照してください。ソースプログラム中で、コメントが英語になっている PIant 1.9 で作成しています。 CodeWarrior Pro 3 と PowerPlant については、それぞれの Macintosh 用のアプリケーションは、メトロワークス社の CodeWarrior pro 3 と power てください。 ■ 11 ・ JPEG 符号化アプリケーション ・プロジェクトの構成 JPEG 符号化アプリケーションプロジェクトは、 プロジェクトウインドウを Fig. 4.3 に示します。 第 4 章実装 ' JPEGCoder " という名称にしました。
do { code くく = S 1 十十 : }while( aHT . SizeTP[k] / / whi le ( 1 ) 1 : s 土房 / / 次のサイズの準備 / / 符号語長を 1 ビット増やす / / サイズを 1 ビット増やす / / si ピットのコードが無いとき / / 繰り返し 最初の符号語はすべてのビットが 0 と決まっています。そこで、符号語 c 。 de を 0 で初 期化します。 s 土は現在作成している符号語のビット数で、初期値はサイズテープルの最 初の要素の値になります。 while( 1 ) で、符号語テープル作成の繰り返しを構成しています。終了はテープルの インデックスが符号語数に達したときです。その判定を土 f ( k > = num ) で行っていま す。 符号語テープルの作成は while( aHT . SizeTP[k] aHT . CodeTP [ k + + ] si ) COde 十十一 / / サイズの確認 / / 符号語の決定 の 2 行で行っています。現在作成している符号語のサイズが s 土ビットです。 k 番目の符号 語は、 aHT. SizeTP[k] ビットでなければなりません。つまり、 aHT. SizeTP[k] si が成り立つ間は、 s 土ビットの符号語の作業になります。 符号語は 2 進数の昇順に並ぶことになっているので、 aHT. CodeTP[k] code として 設定します。このあと、 k + 1 番目の符号語処理のため、 k と code をインクリメントして います。 k 番目の符号語のビット数が現在のビット数より大きくなると、符号語長を変更しなけ ればなりません。その処理を以下の部分で行っています。 do { code < く = 1 ー S 1 十十 : }while( aHT . SizeTP[k] ! = si ) : / / 次のサイズの準備 / / 符号語長を 1 ビット増やす / / サイズを 1 ビット増やす / / s 土ビットの符号語が無いとき / / 繰り返し 符号語は 1 ビット長いものになり、かっ、左側ビットが今までの符号語と異なるパター ンにならなければなりません。そこで、 code を 1 ビット左にシフトして、 s 土 + 1 ビットの 符号語の初期値とします。そして、この符号語のビット数は s 土 + 1 ですから、 s 土をインク リメントします。 場合によっては、あるビット長の符号語が存在しないことがあります。たとえば、 c こ pegc 。 der で使用している輝度 AC 成分用のハフマン符号では 13 , 14 ビットの符号語が ありません。このようなときは、符号語長の更新処理をもう一度しなければなりません。 そこで、 d 。ー wh 土 le ループを構成し、必要な回数だけ繰り返しています。 156 第 4 章実装