JPEGハフマン解析

さて、少し回り道をしています。JPEG解析を行っているのは、あくまでもDICOM XAを解読するためなのです。DICOM XAはシネ画像の動画ですが、いきなり動画は敷居が高すぎます。従って、DICOM XAより抽出した一コマをまともな画像として表示することをまずは第一目標に掲げます。

さて、現段階で何処まで来たでしょうか?

  1. DICOM XAをメモリーに読む込むことができる
  2. 読み込んだメモリーブロックから DHT tagを検出することができる
  3. DHTからハフマン符号を解読することができる

以上でしょうか 従ってこれから行うことは、これらを連携し、さらにメモリーブロックからビット入力して、実際の絵に解読することです。

道は相当に遠いですね。

ハートチーム (Heart Team)

最近、心臓領域の医学界において、ハートチーム (Heart Team)という言葉が声だかに叫ばれています。この言葉は、もともとヨーロッパ心臓病学会、ヨーロッパ心臓外科学会などが共同で提唱した言葉です。一人の心臓病で苦しんでおられる患者さんを治療するに際しては、循環器内科とか心臓外科とかの単科医師が治療方針を決めるのではなく、共同で最適な治療方針を決めねばならない、というものです。そして、拡大した概念としては、これらの診療科医師のみならず、例えば一般内科医、呼吸器内科医、リハビリ診療医師、地域のかかりつけ医師などのみならず、関連するコメディカル、あるいは在宅ケアチームなどがあわさって一人の患者さんのために治療方針を協議して、決定するというものです。

もちちろん、重要なのは患者さん自身の人生観であり、またそのご家族のご意向もあります。

このようにして治療方針を決定し、後はそれを実行する医師が粛々と全力で治療に当たるのです。これは素晴らしい、当たり前だ、そのように誰しも思われると思います。

もちろんそうです。しかし単純に考えて、それを実現するためには、社会が1名の患者さん : 10 – 100名の医療関係者、という関形式を受け入れ、そのシステム維持構築に対して、支出することを許容する必要があります。また、方針決定に関して、実際に治療に当たる医師が全責任を負う必要が無くなるかも知れません。

うーん こう考えると難しい問題ですね。確かに、個々の医師に治療方針決定を任せてしまえば、独善的な治療がまかり通ってしまいます。その中には医学的におかしい治療もなされることでしょう。これは問題です。

ですから、現実にはこれを提唱しだしたヨーロッパでも、全部の患者さんに対してこのハートチームという概念で運用することを主張している訳ではありません。実際の治療として、医学的に完全には立証されていない治療、たとえば左主幹部病変に対する薬剤溶出性ステント植え込み治療とか、糖尿病を有する三枝病変に対する経皮的冠動脈インターベンション治療とか、そのような患者さんに限定して、ハートチームによる意思決定を呼び掛けているのです。

そして、もう一つ重要な疾病治療として、重症大動脈弁狭窄症患者さんに対する TAVI (経カテーテル的大動脈弁植え込み術: 最近では、特に外科系から、TAVR: 経カテーテル的大動脈弁置換術 という呼び名も好んで用いられます)なのです。

当院で、TAVIの治験を開始することに決まった時、僕は色々なことを考えました。

  1. そのような先進的医療を治験で行えることは自分自身とても光栄である
  2. これまで治験に先進的に意欲的に取り組んできたことが生かされた
  3. 世界の最先端医療に乗り遅れることなく、自分のキャリアが追従できる
  4. 開院以来25年間も経たない当院が、このような先進的治療を率先して行える立場になったことは本当に素晴らしい

などのポジティブな感想、そしてその一方でネガティブな感想というか不安

  1. 当院のような純然たる民間病院、何の冠もついていない民間病院で、そのような先進的医療を行うことは概念的に可能だろうか?
  2. そもそも当院にはこのような先進医療を行うだけの様々なリソースがあるのか?
  3. 病院の姿勢として、このような先進医療遂行を受け入れるものだろうか?
  4. 対象となる患者さんが当院のような民間病院に集まるであろうか?

などなどです。そして、そのような不安の中から思った結論は一つ

このような先進的医療を当院のような無冠民間病院が、大学病院や、国立センターなどに伍して行っていくためには、それらの病院を上回る総合力が必要である

ということでした。そして自然に思いついた概念が ハートチームだったのです。

本日二例目はあっという間に治療が大成功に終了しました。そして、患者さんを手術場から ICUに搬送するまでの皆を 治療成功の余韻を味わいながら見ていました。この一人の患者さんの治療のために、循環器科医師4名、心臓外科医2名、血管外科医1名、麻酔科医2名、エコー指導医1名、エコー検査技師2名、レントゲン技師2名、臨床工学士2名、看護師2名、CRC(臨床試験コーディネーター) 2名、その他5名そして外国からの指導医1名、ざっと数えただけでも、これだけ多数の人々が協力して当たったのです。そして、驚くべきことに、皆が楽しみ喜びながら、その準備や後片付けに当たっているのです。チームという言葉で片付けてしまうにはあまりにも深い連帯感がそこにはあるのです。

TAVIを開始し、色々なことを学んでいます。そして、純粋に医学的、あるいは医療技術的なこと以外にも、このようなチームとは? とか そのようなことについても学んでいます。TAVIを開始して、患者さんに対してはとても良いのですが、それ以上に自分自身にとって、本当に素晴らしい体験です。

しかし、本日の二例目、僕の中では壁を越えた感覚があります。これまで数々の新しい医療技術を伴う手技を経験克服してきました。それらの中である時、壁を越えた感覚を味わうことができます。本日はそのような日でした。

 

 

やはり Exciting!!

本日は朝から TAVIです。日本人の Intervetional Cardiologistとしてはトップ・レベルの経験を積みつつある私達ですが、毎回毎回学ぶことが多々あります。

そして何より思うのは、心の強さの必要性です。瞬時の判断力と、それを可能にする心の強さが一番重要な気がします。その次に必要なのは経験でしょう。もちろん、これらの前提には医師としての使命感とか、カテーテル技術の洗練度が必須条件としてあります。

昼の breakの後、また午後あります。最善を尽くし、多くの患者さんに役立てたいと思います。

Google-Syntax

#include <iostream>;

using namespace std;

int main() {
  cout << "Hello World!";
  return 0;
}

Google-Syntaxというコードをハイライトさせるプラグインを入れました。どうやら中国の方が開発されているソフトで、Googleの情報を使用しているらしいのです。Wordpressのプラグインです。

同様のソフトには、有名な SyntaxHighlighterなどがありますが、少し気に入らないのは、Donateして下さい、というボタンが現れることです。たとえば、

といった具合ですどうですか? コードの右側に何やら小さな緑色のボタンが出ています

これに対して Google-Syntaxでは

  1. #include <iostream>
  2. using namespace std;
  3. int main() {
  4.   cout << “Hello World!”;
  5.   return 0;
  6. }

 

のように、出ていないでしょう?

JPEGハフマン・テーブル – 解読実践(17)

さて、ワード単位(16 bits単位)でのサーチはどうなるでしょうか?

これが結構ややこしい、何故ならば今度はワード単位なのでポインターもワードを指すのです。それに考慮すれば

typdef unsigned short u_short;     
  // これでu_shortというワード型を宣言した
std::vector <u_short> address;
  // u_charへのポインタをいれるvectorを宣言した
u_short *buffer = new u_short[bufSize/2]; 
  // ワードずつ確保なので半分
u_short *bufTop = buffer;
u_short *bufEnd = bufTop + bufSize/2;
u_short *bufPntr = bufTop;
 // 動き回るポインターを先頭にセット
fp.read(reinterpret_cast<char *> (buffer), bufSize);
 // ここではバイト単位でしか指定できないのでキャストした
while (bufPntr < bufEnd) {
  if (*bufPntr == 0xC4FF) {
    // DHT tagにヒットした
    address.push_back(bufPntr);
  }
  bufPntr++;
}
fp.close();
delete[] bufTop;

ちなみに u_shortとは unsigned shortであり、C/C++の規約上 16 bits整数を表すものです

JPEGハフマン・テーブル – 解読実践(16)

まずはファイルを読み込み、DHT tagの検出を行う部分を造りました。先日も書いたように、ここでは バイト単位でサーチするか ワード単位でサーチするかで少し変化します。ただ、どう考えてもワード単位サーチの方が速そうですが、理解が簡単なのでまずはバイト単位です。あくまでも標準C++を用いています

#include "stdafx.h"
#include <iostream>
#include <fstream>;
#include <vector>;
typedef unsigned char u_char;

int _tmain(int argc, _TCHAR* argv[])
{	
	if (argc != 2) {
		std::cout << "一つの引数が必要です\n";
		char ch = 'x';
		while (ch != 'e') {
			std::cin >> ch;
		}
		return false;
	}
	std::ifstream fp(argv[1], std::ios::in|std::ios::binary);
	if (fp.fail()) {
		std::cout << "エラー::そのような入力ファイルは存在しません\n";
		char ch = 'x';
		while (ch != 'e') {
			std::cin >> ch;
		}
		return false;
	}

	std::ifstream::pos_type begp, endp, fsize;
	std::vector<u_char *> address; // DHT tag位置記録用
	fp.seekg(0, std::ios_base::end);  // ファイルの最後に移動
	end = fp.tellg();  // endpにはファイルの最後の位置がセット
	fp.seekg(0, std::ios_base::beg);  // ファイルの最初に移動
	begp = fp.tellg();  // begpにはファイルの先頭位置がセットされた
	fsize = endp - begp;  // fsizeにはファイルサイズがセットされた
	long const bufSize = static_cast<long>(fsize);  // bufSizeはバッファの大きさ
	u_char * buffer = new u_char[bufSize];
	u_char * const bufTop1 = buffer1;
	u_char * bufPntr = bufTop;
	fp.read(reinterpret_cast<char *>(buffer), bufSize);	
	u_char * const bufEnd1 = bufTop + bufSize;
	while (bufPntr < bufEnd) {
		if (*bufPnt == 0xFF) {
			if (*(bufPntr+1) == 0xC4) {
				address.push_back(bufPntr);
			}
		}
		bufPntr1++;
	}

	fp.close();
	delete[] buffer;

	std::cout << "\nDHT tagバイト検索\n";
	std::vector<u_char *>::iterator it;
	for (it = address.begin(); it != address1.end(); ++it) {
		std::cout << std::showbase << std::hex << (*it - bufTop) << std::endl;
	}

	char ch = 'x';
	while (ch != 'e') {
		std::cin >> ch;
	}
	return 0;
}

リトルエンディアン

ついにリトルエンディアンについて勉強しました。バイト列をメモリーに置くとき、小さいのを上位アドレスに置くか、下位アドレスに置くか、この違いなのです。どうやら IBM360/370というかつての名コンピューターではビッグエンディアンらしいのですが、現在のマイクロプロセッサーでは Core 7などを含め、インテル系など全てリトルエンディアンなのです。この問題は非常に深い問題であり、その点についての簡単な議論をここで見ることができます。
それはさておき、随分自分の知識というかスキルが進歩アップしたのですが、DHT tagの発見プログラムとして unsigned char * bufferにバイト列が蓄えられ、その中で 0xFFC4というDHT tagを発見するプログラム部分として まず、バイト検索では

unsigned char * bufTop = buffer;
unsigned char * temp;
for (long i = 0; i < bufSize; i++) {
  if (*buffer == 0xFF) {
    // tagマーカーに入った
    temp = buffer;
    buffer++;
    if (*buffer == 0xC4) {
      // DHTタグに入った
      std::cout << "DHT tag founded! ad address: ";
      std::cout << std::hex << temp << std::endl;
      buffer++;
    }
  }
}

となるのですが、これをワード(2 bytes)検索だと

unsigned short * bufTop = buffer;
for (long i = 0; i < bufSize/2; i++) {
  if (*buffer == 0xC4FF) { // DHT tagにヒットした
    std::cout << "DHT tag founded! at address: "; 
    std::cout << std::hex << buffer << std::end;
    buffer++;
  }
}

となるのです。ポインターの ++ 演算子による増加が 1 byteか 2 bytesかに気を付け、そしてリトルエンディアンであることに気をつけねばなりません。

相当悩んだぞ

DHTの読み込みをC++で書いていて どうしても分からないことにぶち当たりました。それは、以下のような簡単なものなのです たと

えば、このようなbinary fileを作成します。

0x42 0x42 0x43 0x44 0xFF 0xC4 0x45 0x46

これはテキストとして表せばASCII Code表に則って ABCD トEF とあらわされると思います。
そこでプログラムを書いたのです。おのずと知れた JPEG DHTのタグ 0xFF 0xC4の連続する 2バイトを検出するのです。

char * buf;
fp.read(buf, 8);
for (int i=0; i<8; i++) {
  if (*buf == 0xFF) {
    std::cout << "DHTタグに入るよ\n";
  }
  std::cout << *buf << " ";
  buf++;
}

当然出力は、A B C D DHTタグに入るよ・・・
となる筈です。ところがならないのです!!!
どう考えても分からないのです。これはC++では何か変数の型チェックが厳しいのかも知れない? そのように思いました。そこで調べると charと宣言するとそれはdefaultでsigned charすなわち -127~+127を保持し、unsigned charが0~255を保持できる、と書いてあります。そんなこと分かっているのですが、ひょっとして、0xFFはsigned charではまずい値なのかも知れません。そこで

typedef unsigned char u_char;
u_char *buf;

としてみました。ところが、今度は fp.read(buf, 8); の部分で、bufをchar*に変換できません、と叱られました。そこで

typedef unsigned char u_char;
u_char *buf;
fp.read(reinterpret_cast<char *>(buf), 8);

このようにすると、無事0xFFを検出でき、

A B C D DHTタグに入るよ
ト E F

とこのように出力されるようになりました。フーッ C++は型のチェックが厳しいですね。

JPEGハフマン・テーブル – 解読実践(15)

さてさてファイルを読み込むことをせねばなりません。C++ではどのようにするか? これも少し調べました 標準C++を用いて書けば以下のようにするのが良いと思います

#include "stdafx.h"  // Windowsだから必要なのね
#include <iostream>
#include <fstream>

int main(const int argc, const char* argv[])
{
	if (argc !=2) {
		std::cout << "エラー::入力ファイル名を指定して下さい\n";
		char ch = 'x';
		while (ch != 'e') {
			std::cin >> ch;
		}
		return false;
	}
	std::ifstream fp(argv[1], std::ios::in|std::ios::binary);  // ファイル・オープン
	if (fp.fail()) {
		std::cout << "エラー::入力ファイルは存在しません\n";
		char ch = 'x';
		while (ch != 'e') {
			std::cin >> ch;
		}
		return false;
	}
	std::ifstream::pos_type begp, endp, fsize;
	fp.seekg(0, std::ios_base::end);  // ファイルの最後に移動
	endp = fp.tellg();  // endpにはファイルの最後の位置がセット
	fp.seekg(0, std::ios_base::beg);  // ファイルの最初に移動
	begp = fp.tellg();  // begpにはファイルの先頭位置がセットされた
	fsize = endp - begp;  // fsizeにはファイルサイズがセットされた
	char* buffer = new char[(int)fsize];  
	if (buffer == NULL) {
		std::cout << "エラー::バッファを確保できませんでした\n";
		char ch = 'x';
		while (ch != 'e') {
			std::cin >> ch;
		}
		return false;
	}
	fp.read(buffer, fsize);  // バッファにファイルから読み込み
        fp.close();
	char * const bp = buffer;
	std::ofstream fpout("Out.txt", std::ios::out|std::ios::binary);
	fpout.write(bp, fsize);  // Out.txtというファイルに書き出し
        fpout.close();
	delete[] buffer;  // バッファの解放
	return 0;
}

何ともはやめんどくさいですね。しかし、仕方無いのです。これだけのことを我々が普段ファイルを読み書きする時に行っているのです。もちろん最終的にはgraphic interfaceを実装するつもりですよ