昨日は

昨日は社会保険 下関厚生病院を訪問させて頂きました。その前に、下関で有名な唐戸市場という、実際の市場でもあり、また観光名所でもある市場で昼食を摂り、それから訪問しました。

慢性完全閉塞二例が準備されていました。一例目は何とかできるかな? と思ったのですが、二例目はとても駄目だ、と思いました。実際に症例を開始したのは、14:00頃でした。一例目は予想よりも難しく、ワイヤー通過の後も、なかなかバルーンを通過させれなかったのですが、最終的には長い病変に対して薬剤溶出性ステントを4本用いて綺麗になりました。そして、次の症例は15:00頃から開始しました。何とか順行性にワイヤーを通過したのですが、これまたバルーン通過に難儀しましたが、最終的には薬剤溶出性ステント2本で綺麗になり、ものすごい嬉しい結果で「あー 良かった」と思いました。これもみな、下関厚生病院の方々のご協力のお蔭です。難しい、と思っていたのですが、結果的には16:00には病院をおいとまさせて頂きました。

本日より西日本

今朝は羽田から山口宇部空港に飛びました。これから下関厚生病院を訪問し、PCIを行わせて頂きます。明日は、大分に移動し、CVIT九州地方会での講演、明後日は岡山に移動し、CVIT中四国地方会での講演です。土曜日には鎌倉に戻ります。

疑問解消

昨日来、Binary Editorを用いて、実際のDICOM fileを解析してきました。そもそも今更これを行っているのは、先日出勤時の自転車で、「あれ? DICOM XAは 1 pixel -> 8 bitsと固定していいのかな?」という疑問からでした。

そうして見直してみると、0x0028 0x0001という Bits Allocatedという DICOM Tagがあるのです。しかし、ここで問題でした。Binary Editorで見てみても、このタグに続くバイト列は

0x28 0x00 0x00 0x01 0x55 0x53 0x02 0x00 0x08 0x00 0x28 0x00 0x01 0x01

という風になっているのです。ちなみに色分けすれば
0x28 0x00 0x00 0x01 0x55 0x53 0x02 0x00 0x08 0x00 0x28 0x00 0x01 0x01

となります。赤字はDICOM Tagです。次の0x55 0x53はアスキーで “US”となります。これは超音波の意味ではなく、unsigned shortの意味であり、C/C++で定義されているデータ型です。つまり16 bits WORDなのです。

そして、次の黄色の部分にそれ以降何バイトが値なのか? が記載され、ここでは2 bytesです。従って、その次の紫色が値となり、値 = 8が得られます。

最初のタグの意味は、Bits Allocatedなので、このデータ列の意味するところは、1ピクセルは、8 bitsで表現されますよ、という意味です。これで、このDICOM XA fileは、1 pixel -> 1 byteで画素が表現されていることになります。

またまたバカやっていた

今朝病院に出勤する自転車を漕ぎながらふと疑問が湧きました それは

JPEG Losless DC成分で用いるハフマン符号は、12 bitsの差分値まで対象としている
それであれば、1 pixel = 8 bitsと固定していてはいけないのではないか?

というものでした。これまでは、DICOM XAではどうせ1 pixel = 8 bits固定と考えていたのですが、もちろそれは大多数の場合正しいのですが、本当に良いのでしょうか?

そこで再び DICOM規格書に戻ると、これはDICOM tabでbits allocatedなどで何ビットを一ピクセルに指定するか? それが指定されているのです。

そこで、DICOM tagを検索したのです。これもSterlingで行いましたが、無い! 無い! そんなタグ無い!でした。いやいやそんな訳無い、と思い、今度はVisualStudioでbinary openして調べて気が付きました。結局、little endianで並んでいるので、0x00 0x28で検索してはヒットしません。0x28 0x00で検索せねばならなかったのです。まったくバカしていましたね。

一日中トラブル処理

昨日はまず、VisualStudio 2010でコンパイルしてもリンカーがエラーを起こす、というトラブルから始まりました。色々調べるとどうやら .NET Framework 4.5がインストールされ、4.0が消えてしまったからのようです。どうしてそんなことになったのか? と言えば、Microsoftのサイトからフリーで今downloadできる、VisualStudio 2012RCをインストールしたからのようです。

結局、.NET Framework 4.5をアンインストールしてから、再度2010をインストールしてこのトラブルを解決、

そうこうしている内に、MacBook ProにインストールしてあるParallels Desktopを立ち上げたところ、HD1が見つからない、従ってWindows7も立ち上げられない、というトラブルに遭遇、これは焦りました。全て消えたのか? です。

少し考えてから、Timemachineを立ち上げ、8/14にあったバック・アップから、Desktopに存在したWindows7.vpmといかうものをリカバーしたところ、解決しました。しかし、それからWindows Updateをしたり、色々で、結局数時間も使うはめになりました。

ビットストリーム (Bit Stream)入力クラス

以前(2008年頃)書いたプログラムを引っ張り出しました。そしてバグを発見したので、少し書きなおしました。もうバグは無いと思いますが・・・

  1. //
  2. // ReadBitStream Class
  3. //// unsigned char * bpBufに sizeバイト置かれたメモリーブロックから
  4. //// bit単位で値を取り出すクラス
  5. //// エラーがあれば、throwするので、読みだす方は try ~~ catch すべき
  6. //// modified on August 25th, 2012 
  7. //// by Shigeru SAITO, MD, FACC, FSCAI, FJCC
  8. //// originally created by Shigeru SAITO in 2008
  9. //
  10.  
  11. #ifndef __READBITSTREAM_H_
  12. #define __READBITSTREAM_H_
  13.  
  14. #include <memory.h>
  15. typedef unsigned char BYTE;
  16. typedef unsigned short WORD;
  17.  
  18. class CReadBitStream
  19. {
  20. private:
  21.  
  22. protected:
  23.     BYTE *mbpRead;            // 読み出しアドレス
  24.     BYTE *mbpEndOfBuf;        // Bufferの終了アドレス
  25.     BYTE mMask;               
  26.  // bit maskであると同時に現在の読み出しビット位置 (MSB = 7, LSB = 0)
  27.     bool mReadable;            // 1: 読み出し可、 0: 読み出し不可
  28.     void IncBuf(void);        
  29. // 読み出しアドレスのインクリメントとアクセス違反のチェック
  30. public:
  31.     CReadBitStream(void);
  32.     CReadBitStream(BYTE *bpBuf, int size);    // 唯一のconstructor
  33.     virtual ~CReadBitStream(void);
  34.     BYTE GetBYTE(void);        // 1 BYTE読み出し
  35.     WORD GetWORD(void);        // 1 WORD読み出し
  36.     void CopyBYTEs(BYTE* bpDest, int n);        
  37. // n BYTEs読みだしてbpDestのアドレス以降にコピーする
  38.     void SkipBYTE(int n);                    // n BYTEs読み飛ばし
  39.     int GetBit(void);                        // 1 bit読みだして返す
  40.     int GetBits(int numOfBits);                // numOfBits数のビットを読みだして返す
  41.     BYTE* GetNextAddress(void);                // 次の読み出しアドレスを戻す
  42.     int ResetBuffer(BYTE* bpBuf, int size);    // bufferを変更する
  43. };
  44.  
  45. #endif __READBITSTREAM_H_

 

以上がheader部分です、そして.cppの方は以下です

  1. #include “ReadBitStream.h”
  2.  
  3. CReadBitStream::CReadBitStream(void)
  4. :mbpRead(0), mbpEndOfBuf(0), mMask(0), mReadable(false) {}
  5.  
  6. CReadBitStream::CReadBitStream(BYTE *bpBuf, int size) {
  7.     mbpRead = bpBuf;                        // 読み出しアドレスをbufferの先頭にセット
  8.     mbpEndOfBuf = mbpRead + size;            // bufferの最終アドレスをセット
  9.     // 状態変数の初期化
  10.     mMask = static_cast<BYTE>(0x80);        // setMSB
  11.     mReadable = true;                        // アクセスエラー無し
  12. }
  13.  
  14. int CReadBitStream::ResetBuffer(BYTE *bpBuf, int size) {
  15.     mbpRead = bpBuf;
  16.     mbpEndOfBuf = mbpRead + size;
  17.     if ((bpBuf == 0)||(size == 0)) {
  18.         mReadable = false;
  19.         return 1;
  20.     }
  21.     mMask = static_cast<BYTE>(0x80);
  22.     mReadable = true;
  23.     return 0;
  24. }
  25.  
  26. CReadBitStream::~CReadBitStream(void) {
  27.     mbpRead = 0;
  28.     mbpEndOfBuf = 0;
  29. }
  30.  
  31. BYTE CReadBitStream::GetBYTE(void) {        // 1 BYTE読み出し
  32.     if (mReadable) {
  33.         if (!(mMask > 0x80)) {                // BYTE途中は不可
  34.             IncBuf();                        // 次のBYTEに進める
  35.             mMask = static_cast<BYTE>(0x80);
  36.         }
  37.         BYTE r = *mbpRead;                // 実際に1 BYTE読みだす
  38.         IncBuf();
  39.         return r;
  40.     } else {
  41.         throw(“Error at GetBYTE”);
  42.     }
  43. }
  44.  
  45. WORD CReadBitStream::GetWORD(void) {        // 1 WORD読み出し
  46.     if (mReadable) {
  47.         if (!(mMask > 0x80)) {                // BYTE途中は不可
  48.             IncBuf();
  49.             mMask = static_cast<BYTE>(0x80);
  50.         }
  51.         WORD r = (static_cast<WORD>(*mbpRead))<<8;    
  52. // 1 BYTE読み出しそれをWORDにcastしてから8bits左シフト
  53.         IncBuf();
  54.         r |= static_cast<WORD>(*mbpRead);    
  55. // さらに1 BYTEを下のBYTEに付加する
  56.         IncBuf();
  57.         return r;
  58.     } else {
  59.         throw(“Error at GetWORD”);
  60.     }
  61. }
  62.  
  63. void CReadBitStream::CopyBYTEs(BYTE* bpDest, int n) {    
  64. // n BYTEsをpbDestに一挙にコピーしてしまう
  65.     if (mReadable) {
  66.         if (!(mMask > 0x80)) {            // BYTE途中は不可
  67.             IncBuf();
  68.             mMask = static_cast<BYTE>(0x80);
  69.         }
  70.         if (mbpRead + n > mbpEndOfBuf) {
  71.             throw(“Error at CopyBYTEs1”);
  72.         }
  73.         memcpy(bpDest, mbpRead, n);
  74.         if ((mbpRead += n) >= mbpEndOfBuf) mReadable = false;    
  75. // 次の読み出しは不可
  76.         return;
  77.     } else {
  78.         throw(“Error at CopyBYTEs2”);
  79.     }
  80. }
  81.  
  82. void CReadBitStream::SkipBYTE(int n) {            // n BYTEs読み飛ばす
  83.     if ((mbpRead + n) > mbpEndOfBuf) {
  84.         throw (“Error at SkipBYTE”);
  85.     }
  86.     if ((mbpRead += n) >= mbpEndOfBuf) mReadable = false;    
  87. // 次の読み出しは不可
  88.     mMask = static_cast<BYTE>(0x80);
  89.     return;
  90. }
  91.  
  92. int CReadBitStream::GetBit(void) {                // 1 bit読みだす
  93.     if (mReadable) {
  94.         int r;
  95.         r = (*mbpRead > mMask) ? 1:0;
  96.         mMask >>= 1;
  97.         if (mMask == 0x00) {
  98.             mMask = static_cast<BYTE>(0x80);
  99.             IncBuf();
  100.         }
  101.         return r;
  102.     } else {
  103.         throw(“Error at GetBit **”);
  104.     }
  105. }
  106.  
  107. int CReadBitStream::GetBits(int numOfBits) {    
  108. // numOfBits読みだす (0 < n <= 16)
  109.     if ((numOfBits <= 0)||(numOfBits > 16)) {
  110.         throw(“Error at GetBits of numOfBits”);
  111.     }
  112.     if (!mReadable) throw(“Error at GetBits Initial”);
  113.     int r = 0;
  114.     while (numOfBits) {
  115.         if (mReadable) {
  116.             if (mMask == 0x00) {
  117.                 mMask = static_cast<BYTE>(0x80);
  118.                 IncBuf(); 
  119.             }
  120.             r <<= 1;
  121.             r |= ((*mbpRead > mMask) ? 1: 0);
  122.             mMask >>= 1;
  123.             numOfBits–;
  124.         } else {
  125.             throw(“Error at GetBits”);
  126.         }
  127.     }
  128.     return r;
  129. }
  130.  
  131. void CReadBitStream::IncBuf(void){                           
  132.  // buffer読み出しを1 BYTE進める
  133.     if (++mbpRead >= mbpEndOfBuf) mReadable = false;       
  134.  // 次は読み出し不可にする
  135. }

どうでしょうか? 自分で書いたテスト・プログラムでは大丈夫なようです。エラーがあれば、きちんとthrowされています。

キャストだらけのプログラム

型制約の厳しい言語では必要に応じて強制的な型変換(キャスト: Cast)が必要となり、それがまたバグの温床であるらしいのです。

C言語では簡単に型を(  )でくくるにことにより、このキャストが可能です。しかし、これはあまりにも簡単過ぎ、安易に行ってしまいますし、後でバグとりの時に発見しにくい、という副作用もあるらしいのです。

これに対して、C++ではややこしい構文をわざと使用せねばなりません。これにより、プログラマに対して心理的圧迫を与え、何とかしてキャストを使用せずにもっとスマートなプログラムを書くように暗黙的に強制します。また、バグ発見もこの構文を探すことにより、より容易にできる、このような利点が言われています。

こんなこと、自分ごときが書く小さなプログラムでは関係無いよ、そのように今まで思っていましたが、先の、DHTをサーチして、そのアドレスを可変配列Vectorに収納し、後から、そのアドレス近傍の内容を表記するプログラムを書いた時に、そのキャストの嵐に会いました。

  1.     std::vector <u_short *> address;
  2.     // u_charへのポインタをいれるvectorを宣言した
  3.     u_short *buffer = new u_short[bufSize/2]; 
  4.     // ワードずつ確保なので半分
  5.     u_short * const bufTop = buffer;
  6.     u_short * const bufEnd = bufTop + bufSize/2;
  7.     u_short *bufPntr = bufTop;
  8.     // 動き回るポインターを先頭にセット
  9.     fp.read(reinterpret_cast<char *> (buffer), bufSize);
  10.     // ここではバイト単位でしか指定できないのでキャストする
  11.     while (bufPntr < bufEnd) {
  12.         if (*bufPntr == 0xC4FF) {
  13.             // Little Endianに注意!!
  14.             // DHT tagにヒットした
  15.             address.push_back(bufPntr);
  16.         }
  17.         bufPntr++;
  18.     }
  19.     fp.close();
  20.     u_char * bufferByte;
  21.     // これでvector<u_short *> addressにDHTアドレスが格納された
  22.     std::vector<u_short *>::iterator it;;
  23.     for (it = address.begin(); it != address.end(); ++it) {
  24.         std::cout << std::hex << (int)*reinterpret_cast<u_char *> (*it) << ” “;
  25.         std::cout << static_cast<int>(*(reinterpret_cast<u_char *> (*it) + 1)) << ” “;
  26.         std::cout << static_cast<int>(*(reinterpret_cast<u_char *> (*it) + 2)) << ” “;
  27.         std::cout << static_cast<int>(*(reinterpret_cast<u_char *> (*it) + 3)) << ” “;
  28.         std::cout << static_cast<int>(*(reinterpret_cast<u_char *> (*it) + 4)) << ” “;
  29.         std::cout << static_cast<int>(*(reinterpret_cast<u_char *> (*it) + 5)) << ” “;
  30.         std::cout << static_cast<int>(*(reinterpret_cast<u_char *> (*it) + 6)) << ” “;
  31.         std::cout << (int)*(reinterpret_cast<u_char *> (*it) + 7) << ” “;
  32.         std::cout << (int)*(reinterpret_cast<u_char *> (*it) + 8) << ” “;
  33.         std::cout << (int)*(reinterpret_cast<u_char *> (*it) + 9) << ” “;
  34.         std::cout << (int)*(reinterpret_cast<u_char *> (*it) + 10) << std::endl;
  35.         std::cout << ” : “ << (int)*(bufferByte + 4) << std::endl;
  36.     }

こんなことになると、流石に自分の書いているプログラムは改良の余地がたくさんあるぞ、ということを思い知らされますよね。要するに、バイト単位で動かねばならないところを、わざわざワード単位で書いているからこんなことになるのでしょう。流石に31行から35行ではたまらなくなり、C言語のキャスト構文 (int)を static_cast<int>のかわりに使ってしまいました。
これもC++の新しいキャスト構文のお蔭様様ですかね。

これから羽田空港

今 羽田空港に移動中です。途中病院に寄り、昨日の TAVIの患者さん達の具合を伺ってきました。お二人ともお元気で、この治療法のすばらしさがまたしても証明されました。

昨日技術的に学んだことですが、我々が使用しているシステムは Self-expandableのものです。もちろんシースによりプロテクトされており、体温により温まった Nitinol合金姓のステントが大動脈基部で自然に拡張することにより、固定されます。頭の中では、その部位でシースを抜けば良い、これだけです。簡単なことです。

しかし、患者さんの体内では話が異なります。

  1. 心臓が強く拍動している – これに対しては rapid pacingである程度対応します
  2. 大腿動脈アプローチの場合には、長い経路、しかも大動脈弓で強く屈曲しているため、その部位で保護シースと大動脈との摩擦抵抗が大きい、しかもこれも心拍動により変化する
  3. 同様の理由により保護シースとステントの間の摩擦抵抗も大きい
  4. 同様の理由によりシステムと、stiff wireとの間の抵抗も大きい

実際の植え込みでは、これらの相反するいくつもの効力に逆らって指摘部位で植え込みを行わねばならないのです。そながら作用反作用をうまいこと魔法のようにコントロールすることが必要なのです。これにはどうやらセンスと経験が必要な気がします。それにようやく気が付いたのです。これはPCIとは全く異なる世界です。術者として技術的な部分で非常に惹かれる部分がありますし、自分にとってのチャレンジでもあります。

DICOM XAハフマン解析

DICOM XAのファイルを Binary Editorで調査すると どうやら 一コマ静止画の集まりのようです。


以上のように、SOIに始まり、内部にDHTを含み、それに引き続いてSOF3があり、最後にEOIで終わるブロックが一コマとなります。

ちなみに、JPEG規約により、soi = 0xFFD8, DHT = 0x FFC4, SOF3 = 0xFFC3, EOI = 0xFFD9なのです。ちなみに、それぞれ Start of Image, Define Huffman Table, start of Frame 3 = Lossless, End of Imageのことです。

手持ちのDICOM XA fileを調べると、SOIからEOIのブロックに 149.646KBぐらいが含まれていました。1 pixel = 8 bitsの濃度とすれば、1 Frame = 512 x 512 pixelとすれば、262.144 KBのデータ量ですので、42%までデータが圧縮されたことになります。

プログラムとしては、SOF3の次から、EOIの前までをイメージ圧縮データと考え、DHTより求めた解読表に沿って解読すれば良いことになりそうです。