医療画像(DICOM)のバイナリデータを読み込んで表示させるプログラムを作っているのですが、非常に遅い(約4秒)ので改善したいのです。
おそらく、ループ中での、エンディアンの変換とPixelへの張付けが原因と思うのですが、改善方法が判りません。宜しくお願いします。
BolandC++Builder6,Pentiam4,1Gメモリ,XPの環境です。
DICOMO画像のファイルサイズは約2053kB
画像データは1024*1024の16ビットです。
__________________________________
Byte bb[2097152];
int iImage[512][512];
word c,wData;
fp=fopen("filename","rb");
setvbuf(fp,NULL,_IOFBF,4096*1024*1024);
while(gData!=0xE07F) //グループタグの検索//
fread(& gData,2,1,fp);
while(eData!=0x1000) //エレメントタグの検索//
freadd(& eData,2,1,fp);
fread(&wData,2,4,fp); //空読み//
fread(bb,1,2097152,fp); //画像データ//
fclose(fp);
for(y=0;y<=512;y++){
for(x=0;x<=512;x++){
wData=256*bb[4*x+4096*y]+bb[4*x+4096*y+1];
c=wData*256/4096;
iImage[x][y]=c;
Image->Canvas->Pixels[x][y]=(TColor)((c<<16)|(c<<8)|c);
c=0;
}
}
No.8ベストアンサー
- 回答日時:
原因は幾つか推測できるだろうと思います。
が、ここでやるべきは闇雲にここが怪しいからいじってみよう
という場当たり的な対処ではなく、
きちんとプロファイリングをして、どこがホットスポットなのかを
見極めることだと思います。
もっともC++ Builderだとまともに使えるプロファイラがあるのかどうか
わかりません(無責任ですみません)。
少なくとも無料で使えるものはないかも知れません。
この回答への補足
遅くなりましたがご意見を参考にしてclockで処理時間を計測しました
default 3.00s
配列を[y][x] 2.47s
#16#17 2.49s
Canvas->Pixel[x][y]を外し、新たな配列に入れた場合 0.062s
となりました。
Canvas->Pixel[x][y]の処理が遅い原因のようですが、ループ内ではBitmapの画素値の計算だけにして、ループ外でBItmapの配列を直接imageに貼付ける処理を試してようと思いますが、方法が判りません。
No.18
- 回答日時:
┌色々検索していたら,面白いものを見つけました.
│DICOM ファイルフォーマットのわかりやすい説明があります.
↓
DR研究会誌
pp.189~222 よくわかるDICOM画像の取得方法 ‐自作プログラムによる方法‐
http://www.kit.hi-ho.ne.jp/dr-study-group/naiyou …
これを読んで,いくつか疑問点が出てきました.
(1) #5,#14 のお礼欄に「うまくいかない」と書かれていますが,
どう「うまくいかない」んでしょうか?
・画像の形はそれらしいが,階調がおかしい.
・似ても似つかない画像が表示される.
(2) ご質問は高速化方法となっていますが,元のプログラムでは
ちゃんと画像が表示されていたのでしょうか?
#14 のお礼欄に「表示だけなら … OK」とありますが,
これは画像ファイルを読んで表示させたということでしょうか?
(3) #14 のお礼欄に「リトルエンディアンのはず」とあるので,
ファイルのフォーマットはおそらく "Implicit VR Little Endian"
とやらになるのだと思いますが,その場合グループタグや
エレメントタグもリトルエンディアンなのでしょうか?
もしそうだとしたら,グループタグとエレメントタグの検索部分
には間違いがあります.グループタグは PIXEL_GROUP=0x7FE0,
エレメントタグは PIXEL_DATA_ELEMENT=0x0010 となっているので,
これをそのまま (エンディアン反転させずに) 検索すべきです.
つまり 0xE07F → 0x7FE0,0x1000 → 0x0010 に訂正が必要.
(#9 のお礼欄の URL の Fig.8 参照.)
(4) 上記 URL (DR研究会誌の方) の204~205ページによると,
Implicit VR のヘッダは,次の4つの繰り返しだそうです.
(a) Group Tag (2バイト)
(b) Element Tag (2バイト)
(c) Value Length (4バイト)
(d) Data (Value Length バイト)
ところで,ご質問のプログラムでは,エレメントヘッダを読んだ
直後に8バイトの空読みをしています.wData が2バイトなのに
8バイト読み込んでいるのでアクセス違反になるという問題も
ありますが,それは置いといて….
Implicit VR の Value Length は4バイトですが,8バイト読んで
いるのは単なる4バイトの間違い? それとも8バイトが正しいと
すると,Explicit VR フォーマットの "OB,OW,SQ,US の場合"
というのに相当しているんでしょうか?
(5) 画像データより前の Data 部にバイナリデータが入ることは
ないんでしょうか? もしあるとすると,その中にたまたま
グループタグやエレメントタグと同じ2バイトがあった場合に
「偽物」をつかまされてしまいます.
(6) Value Length の値は奇数になることはないんでしょうか?
グループタグとエレメントタグの検索は2バイト単位で読んで
いるので,奇数バイトの Data があると検索に失敗する可能性
があります.
画像ファイル内にある,画像データの本体の開始位置前後数十バイト
の16進ダンプを見せていただけると,色々わかるかもしれません.
この回答への補足
お礼の書き込み訂正します。Wordで持ってきて
E07F,1000,574F,0000,2000,0000,......................です。
お世話になっております。
私の勘違いが判りました。各装置のデータはリトルエンディアンなのですが、画像サーバがビックエンディアンに変換してました。
(1)(3)(4)上記理由によりお騒がせいたしました。
ちなみに画像データ前の16進並びは
E0 7F 10 00 57(W) 4F(O) 00 00 20 00 00 00
画像が22 00 20 00............です。
(2)正常に表示できました。
(5)(6)そういう場合もあるかもしれませんが、CT,MRI,透視,カラー画像のDICOM変換の4つでは2byte区切りです。これらの画像に対応できれば当面は問題ありません。
No.17
- 回答日時:
★あぁ、済みませんでした。
・訂正前⇒Word (*lpBuff)[ 1024 ] = (Word*)bb; ←これを宣言
訂正後⇒Word (*lpBuff)[ 1024 ] = (Word(*)[1024])bb; ←これを宣言
でした。
No.16
- 回答日時:
★回答者 No.1 です。
・補足要求、有り難うございます。
ようやく質問ソースを把握できましたので、繰り返し部分を中心にサンプルを載せます。
サンプル:
int iImage[ 512 ][ 512 ];
Byte bb[ 2 * 1024 * 1024 ];
Word (*lpBuff)[ 1024 ] = (Word*)bb; ←これを宣言
Word wData;
if ( (fp = fopen("filename","rb")) != NULL ){
/*
画像データを読み込む
*/
fclose( fp );
}
for ( y = 0 ; y < 512 ; y++ ){
for ( x = 0 ; x < 512 ; x++ ){
wData = lpBuff[ y * 2 ][ x * 2 ] >> 4; ←2ピクセル毎に12Bit→8Bit(4096→256階調)
iImage[ x ][ y ] = wData;
Image->Canvas->Pixels[ x ][ y ] = (TColor)(wData * 0x010101); ←グレイスケール変換
}
}
最後に:
・本当は iImage[x][y] は iImage[y][x] にすべきです。
一般に C 言語で2次元配列は横方向を次元の低い方で表し、縦方向を高い次元で表します。
多分、もうすべてを [x][y] と操作するように設計していると思うので今度、プログラムを
行うときには気をつけて下さい。本当は [y][x] 操作に直すべきだと思っていますが…。
・以上。一通り動作するか、確認をお願いします。
Word (*lpBuff)[ 1024 ] = (Word*)bb; ←これを宣言
の部分で以下のエラーがでました。
[C++ エラー] E2034 'unsigned short *' 型は 'unsigned short ( *)[1024]' 型に変換できない
No.15
- 回答日時:
>もし「1画素16ビットの1024×1024を、間引きして、
>8ビット256階調の512×512にする」んで正しければ、
>以下のようになる。
間引処理
wData=*p++;
↓
wData=*p;
p+=2;
No.14
- 回答日時:
Intel系のCPUを使用していて、画像データが16bitの数値なら
下位8bit, 上位8bitの順に格納されるので、
wData=256*bb[n]+bb[n+1];ではなく
wData=bb[n]+256*bb[n+1];となるのですが?
また
bb[4*x+4096*y]、bb[4*x+4096*y+1]としている所からすると
4バイト毎に頭2byteを取得するという事でしょうか?
4×8bit=32bit ≠ 16bit(?)
Intel系のCPUで画像データが2byte(16bit)の場合であれば
下記の様なコーディングも可能です。
short *p, c, wData;
p=(short *)bb;
for(y=0;y<512;y++){
for(x=0;x<512;x++){
wData=*p++;
c=wData/16; // 256/4096 => 1/16
iImage[y][x]=c; //?? [x][y]
Image->Canvas->Pixels[y][x]=(TColor)((c<<16)|(c<<8)|c);//??
c=0;
}
}
回答ありがとうございます。
ご指摘の通り、リトルエンディアンのはずなのですが、それだとうまくいかないのです。どこか2重に処理しつじつまが合っているのでしょうか?表示だけならCT装置と透視装置のDICOMデータで確認してOKなのです。
bb[4*x+4096*y]以下は元画像サイズ1024*1024を512*512にリサイズしています。
No.13
- 回答日時:
>(2)『c = wData * 256 / 4096;』の部分は下位バイトの上位4ビットを
> 取り出して c に4ビットを代入するのですか。→c = ((wData << 8) >> 12); と考えた。
No9の考え通りに下位バイトの上位4bitを出すなら良く考えたらこうかも
c= (bb[4*x+4096*y]>>4) & 0xf;
---------------以下ただのメモ
wData=256*bb[4*x+4096*y]+bb[4*x+4096*y+1];
c=wData*256/4096;
↓
c = (256*bb[4*x+4096*y]+bb[4*x+4096*y+1]) *256/4096;
c = (256*bb[4*x+4096*y] + bb[4*x+4096*y+1]) /16;
c = 16*bb[4*x+4096*y] + bb[4*x+4096*y+1]/16
あれ?合わないぞ?(゜∀。)
私の質問に付き合っていただきましてありがとうございます。
*256/4096の部分はデータの数値0から4096をRGB256階調に変換している部分です。
手元の本でRGBは256階調でとあったので。
No.12
- 回答日時:
#3 です.
可能ならば,iImage[x][y] のxとyを入れ替えて iImage[y][x] のように
した方が,いくらか速くなる可能性はあると思います.
なぜならば,ご質問のプログラムの二重ループ部分がアクセスしている順番
どおりに iImage[][] の要素がメモリ上に並ぶことになり,キャッシュの効きが
良くなると考えられるからです.(bb[] の方は既にそうなっています.)
現在のままでは iImage[][] のあるメモリ領域はランダムアクセスされているので,
キャッシュはあまり効いていない (むしろキャッシュライン単位で読み書きするので
逆効果になっている) のではないかと思われます.
もっとも,iImage[][] のxとyを入れ替える場合には,#10 のコードも改造が必要です.
(もっと簡単になります.)
No.11
- 回答日時:
★回答者 No.1、No.9 です。
・繰り返し文の最適化は、他の回答者さんのを参考にして下さい。
私はヘッダ部についてアドバイスします。
そう思いましたが質問のソースから考えると gData=0xE07F、eData=0x1000 が常に
入ってしまいませんか?
・検索となっていますが、fp で続けて fread() 関数で読み込んでいますよね。
検索後にデータを読み飛ばしているようでもないので、ここの部分が良く理解できません。
アドバイスするにあたり、医療画像(DICOM)のバイナリデータの詳細を教えてくれませんか?
また補足要求です。何度もすみません。
No.10
- 回答日時:
#3 です.
#5 さんのコードを元に,iImage[x][y] の2次元アクセスをしないようにしたバージョンです.
でも,どれほど効果があるかはわかりません.やはり実行時間を測定しないと….
(Image->Canvas->Pixels[x][y] の2次元アクセスは残したままだし.)
const Byte *src = bb;
int *destLine = &iImage[0][0]; // iImage の走査線の左端 iImage[0][y] を指す.
int *dest;
int x, y;
for(y = 0; y < 512; y++) {
dest = destLine; // == &iImage[0][y]
for(x = 0; x < 512; x++) {
wData = (src[0] << 8) | src[1];
#if 1
// こっちでいいのかな?
c = wData >> 4;
#else /* 0/1 */
// ダメなら元のまま.
c = wData * 256 / 4096;
#endif /* 0/1 */
*dest = c;
Image->Canvas->Pixels[x][y] = (TColor)(c * 0x010101);
src += 4; // 1画素読み飛ばす.
dest += 512; // == &iImage[x + 1][y]
}
src += 2048; // 1行読み飛ばす.
destLine++; // == &iImage[0][y + 1]
}
> (fread() についても.)はどういう意味でしょうか?
fread(),fopen() などはディスク I/O を行うので,途中でエラーが起きる
可能性もあります.それにファイルが壊れていたり,間違ったファイルを
指定したりすると,途中で読み込むべきデータがなくなることもあります.
例えばご質問のプログラムでは,ファイル内にグループタグかエレメントタグの
どちらかがない場合,無限ループします.
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・一回も披露したことのない豆知識
- ・これ何て呼びますか
- ・チョコミントアイス
- ・初めて自分の家と他人の家が違う、と意識した時
- ・「これはヤバかったな」という遅刻エピソード
- ・これ何て呼びますか Part2
- ・許せない心理テスト
- ・この人頭いいなと思ったエピソード
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・あなたの習慣について教えてください!!
- ・ハマっている「お菓子」を教えて!
- ・高校三年生の合唱祭で何を歌いましたか?
- ・【大喜利】【投稿~11/1】 存在しそうで存在しないモノマネ芸人の名前を教えてください
- ・好きなおでんの具材ドラフト会議しましょう
- ・餃子を食べるとき、何をつけますか?
- ・あなたの「必」の書き順を教えてください
- ・ギリギリ行けるお一人様のライン
- ・10代と話して驚いたこと
- ・家の中でのこだわりスペースはどこですか?
- ・つい集めてしまうものはなんですか?
- ・自分のセンスや笑いの好みに影響を受けた作品を教えて
- ・【お題】引っかけ問題(締め切り10月27日(日)23時)
- ・大人になっても苦手な食べ物、ありますか?
- ・14歳の自分に衝撃の事実を告げてください
- ・架空の映画のネタバレレビュー
- ・「お昼の放送」の思い出
- ・昨日見た夢を教えて下さい
- ・ちょっと先の未来クイズ第4問
- ・【大喜利】【投稿~10/21(月)】買ったばかりの自転車を分解してひと言
- ・メモのコツを教えてください!
- ・CDの保有枚数を教えてください
- ・ホテルを選ぶとき、これだけは譲れない条件TOP3は?
- ・家・車以外で、人生で一番奮発した買い物
- ・人生最悪の忘れ物
- ・【コナン30周年】嘘でしょ!?と思った○○周年を教えて【ハルヒ20周年】
- ・10秒目をつむったら…
- ・人生のプチ美学を教えてください!!
- ・あなたの習慣について教えてください!!
- ・都道府県穴埋めゲーム
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
UTF-8で5~6バイトになる文字コ...
-
10Mバイトて文字数に すると何...
-
エクセルシート名の制限を変更...
-
CRC計算方法
-
3バイト文字(UTF-8)をprintfで...
-
COBOLのCOMP形式について
-
VB6で漢字の1バイト目か2バイト...
-
char str[256]の256の意味は?
-
64bit対応
-
CD R の書き込み
-
C言語でファイルをよんだあとの...
-
ビットスワップとバイトスワッ...
-
GetWindowTextでアドレスバーか...
-
C言語において2バイト文字を検...
-
C# で MSSQL から XML 出力で、...
-
memcmp バイナリデータの比較方法
-
100MB
-
PICマイコンで変数値の保持
-
sedコマンドで全ての2バイト文...
-
VBAでUnicodeしか存在しない文...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
エクセルシート名の制限を変更...
-
UTF-8で5~6バイトになる文字コ...
-
10Mバイトて文字数に すると何...
-
COBOLのCOMP形式について
-
char str[256]の256の意味は?
-
バイト列とバイナリ列の違いが...
-
ピクセル,dpiから容量(バイト...
-
「1TB」のHDDに日本語は何字入...
-
Javaで日本語1文字のバイト数
-
機種依存文字をチェックしたい。
-
PICマイコンで変数値の保持
-
【VB2005】テキストボックス内...
-
SQLで1バイト、2バイト混在...
-
ビットスワップとバイトスワッ...
-
VBAでShift-JISのURLエンコード
-
GetWindowTextでアドレスバーか...
-
C++ Builderで文字列をバイトに...
-
64bit対応
-
UCS-2の一覧表が欲しい
-
VB.NET LeftBの代用
おすすめ情報