ハフマン符号表は完成したものの

一昨日ハフマン符号表解読プログラムが完成したものの、いくつかのDICOM XA fileを解読していたらこんな符号表に出くわしました

実際の DICOM XAより抽出したハフマン符号表

あれあれ?という感じです 何故ってビット長が何と値 16まであるのです。だって、DICOM XAの1 pixelは 8bitsの深度の筈です。実際のこのファイルのBits Allocatedなどを調べても8 bitsとしてしか記載は無いのです。だとすると、その値の差分も8 bits以内に収まらねばなりません。これってどういうこと? 何で 16 bitsあるの?

誰か御存知の方 ご指導頂けませんでしょうか? 大混乱しています。

これから沖縄で日中友好TRIセミナー開催

今 羽田空港 これから那覇に飛び、当地 沖縄南部徳洲会病院を舞台に、日中友好TRIセミナーを開催します。日本とアジア近隣諸国、特に沖縄近海では、このところ国土をめぐるいさかいが続きます。その中で、開催するのはとても神経を使いますが、これで止めれば負けたことになります。強い気持ちで乗り越えるのです。
那覇まで飛びますが、一泊のみで明日の夕方には現地を飛び立ち戻ります。

そしてついにハフマン符号表が完成です

そらに改良しました。ハフマン符号と、その値の組み合わせ、つまりハフマン符号表を完成させました。もちろん実際のDICOM Viewerにおいては、こんな表をいちいち見せません。これはあくまでもプログラムのチェック用にわざわざ出力させているのです。
実際のDICOM XA fileを読み込み、DHTを解読し、ハフマン符号とそれが示す値(この値は、続く何ビットが実際の値かを示しています)のペアの表を作成することに成功しました。それが以下のものです

ハフマン符号とその値の出力結果

そうなると再び登場

そうなると、出力チェックのために 少し仕掛けが必要です。これも大分前に作成したプログラム部品です

#ifndef __OSTREAM_H_
#define __OSTREAM_H_
// 二進出力を可能とした ostream (Programmedd by Shigeru SAITO, MD on June 5th, 2009)
// modified by S. SAITO, MD on December 14th, 2010
// 使い方: このheader fileをインクルードし、名前空間ostを用いてcoutに出力する
// ost::ostream cout; とcoutを生成し、
// cout << value;
// のように使う、そして標準出力については std::cout << std::endl; のようにする
// 入力の型(BYTE, WORD, int)に応じて自動的に8桁、16桁で出力するがsetw()で出力桁数を変更できる
#include <iostream>
#include <iomanip>
#include <string>
typedef unsigned char BYTE;
typedef unsigned short WORD;

namespace ost {
	enum e_manip {bin, hex, dec, endl};
	class setw {
		int width_;
		setw() {}	// 引数無しコンストラクタ禁止
	public:
		setw(int n);
		int get_width(void) const;
	};

	class ostream {
	private:
		int width, bitWidth;
		e_manip mManip;
		bool mIsSetW;
		void setWidth(int w);
		void out(int n);
	public:
		ostream();
		ostream & operator << (const setw & sw);
		ostream & operator << (const e_manip em);
		ostream & operator << (const BYTE n);
		ostream & operator << (const WORD n);
		ostream & operator << (const int n);
		ostream & operator << (const char* s);
		ostream & operator << (const std::string str);
	};

};	// namespace ost

何と 2009年6月に作ったものなのですね。二進出力を可能とするクラスです。どうしてC++やCに二進出力が標準的に備わっていないのか不思議で仕方ありません。普段使わない八進出力なんて存在するのに・・・です。

遅々として、しかし着実に進展

DICOM XA Viewerの作成プロジェクト何処までいきましたかね?

もちちろん自分は一義的には Interventional Cardiologistであり、医師であり、プログラマではありませんので、なかなか進みません。そして、何よりも心の問題です。これまで、何年も目標として頑張ってきて、心の何処かに何時も、解けない難問として横たわっていたものが、どうやらもうすぐ解けそう、と思った時、「あー、これで目標が無くなるのか?」という恐れも湧いてきたのです。それが、前に進むのを妨げているのです。

とは言っても、本当に解けるのか? それは実現せねばわかりません。という訳でゆっくりとタラタラやっています。まずは、ファイル読み込みをメモリー中のバッファに展開するクラスを作成しました。といっても今までのクラスを一つにまとめただけですが・・・

このクラスでは、ファイルを読み込み、エラーがあれば、エラー送出クラスでエラーを送り、無ければメモリー中のバッファに展開し、ビット入出力のためのインターフェースを提供します。実装して、基礎的なテストを行い、作動することを確認しています。ヘッダ部分のみ掲載しましょう。

class CBitBuffer
{
protected:
	BYTE *bufTop;			// Bufferの開始アドレス
	BYTE *bufEnd;			// Bufferの終了アドレス
	long bufSize;			// Bufferのサイズ
	BYTE *mbpRead;			// 読み出しアドレス
	BYTE mMask;				// bit maskであると同時に現在の読み出しビット位置 (MSB = 7, LSB = 0)
	bool mReadable;			// 1: 読み出し可、 0: 読み出し不可
	void IncBuf(void);		// 読み出しアドレスのインクリメントとアクセス違反のチェック

public:
	CBitBuffer(void);
	CBitBuffer(const int argc, const char* argv);	// ファイルを読み込んでbufferに収納
	virtual ~CBitBuffer(void);
	BYTE *getBufTop(void);		// 内部bufferの先頭アドレス
	long getFileSize(void);		// 内部bufferの大きさ
	BYTE *getBufEnd(void);		// 内部bufferの最終アドレス

	BYTE GetBYTE(void);		// 1 BYTE読み出し
	WORD GetWORD(void);		// 1 WORD読み出し
	void CopyBYTEs(BYTE *bpDest, int n);		// n BYTEs読みだしてbpDestのアドレス以降にコピーする
	void SkipBYTE(int n);					// n BYTEs読み飛ばし
	int GetBit(void);						// 1 bit読みだして返す
	int GetBits(int numOfBits);				// numOfBits数のビットを読みだして返す
	BYTE *GetNextAddress(void);				// 次の読み出しアドレスを返す
};

C++ std::cout で char * で示されるアドレスを表示するには?

unsigned char * で表される変数、たとえば

unsigned char * p = 'x';

と宣言した場合、変数pが指すのは’x’という文字が格納されているメモリーのアドレスです。そして、そのアドレスをを用いて表示するとします たちえば

#include 

int main(void) {
  unsigned char * p = 'x';
  std::cout << std::hex << p << std::endl;
  return 0;
}

とします。結果は、たとえば、0023456 とか表記して欲しい訳です。もちろん、この数字は勝手に書いたものですが、要するにメモリーアドレスのつもりです。
しかし、このようにしても何も表示されません。
いくらやっても分かりませんでしたが、この質問箱を読んで分かりました。
要するに、<ostream>には operator<<(const char*)という演算子オーバーロードが定義されているらしいのです。 従って、ここは’x’という文字列を出力するべきなのか、あるいはアドレスを出力すべきなのかが不定となり、何も出力されなくなるらしいのです。

このため、アドレスを出力するためには、演算子オーバーロードされていないもの、void*などにキャストせねばならないらしいのです。以下のようにです

#include 

int main(void) {
  unsigned char *p = 'x';
  std::cout << std::hex << reinterpret_cast<void *>(p) << std::endl;
  return 0;
}

のように、void*にキャストする必要があるらしいのです。勉強になりました。