医療画像(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で質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# このプログラミング誰か教えてくれませんか 1 2022/06/02 15:27
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
- PHP PHPでテキストファイルに保存した時の改行問題 1 2022/11/19 15:07
- C言語・C++・C# バイナリファイルをコピーするのにかかる時間を測りたいのですが実行するとFatel error:gli 2 2022/11/03 01:10
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- Excel(エクセル) VBA フォルダ見える化のコードについて 2 2023/06/19 15:04
- フィナンシャルプランナー(FP) FP検定は「エフピーけんてい」で読み方会ってますか汗 2 2022/04/20 22:37
- C言語・C++・C# C言語について教えて欲しいです。 ファイルの中身をコピーするプログラムを作ってます aというファイル 7 2022/11/03 19:21
- Excel(エクセル) EXCELのグラフを画像(JPG形式)で保存、通常実行がうまく行かない。ステップインはうまく行く 3 2022/08/30 12:06
- その他(プログラミング・Web制作) uwscとWinShotを使いスクリーンショットを撮る 1 2022/06/30 21:15
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
10Mバイトて文字数に すると何...
-
バイナリとBCDコード
-
UTF-8で5~6バイトになる文字コ...
-
COBOLのCOMP形式について
-
エクセルシート名の制限を変更...
-
char str[256]の256の意味は?
-
VBAでUnicodeしか存在しない文...
-
バイト列とバイナリ列の違いが...
-
SQLで1バイト、2バイト混在...
-
1KBが1024byteな理由
-
日付時刻を4バイトに
-
URLは最高何文字まで可能なので...
-
Javaで日本語1文字のバイト数
-
ビットスワップとバイトスワッ...
-
文字数を取得したい
-
3バイト文字(UTF-8)をprintfで...
-
ピクセル,dpiから容量(バイト...
-
VB2008 2バイト文字の化け字...
-
【VB2005】テキストボックス内...
-
Visual Basicでパック10進(2進...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
UTF-8で5~6バイトになる文字コ...
-
10Mバイトて文字数に すると何...
-
エクセルシート名の制限を変更...
-
COBOLのCOMP形式について
-
char str[256]の256の意味は?
-
バイナリとBCDコード
-
「1TB」のHDDに日本語は何字入...
-
Javaで日本語1文字のバイト数
-
バイト列とバイナリ列の違いが...
-
SQLで1バイト、2バイト混在...
-
ピクセル,dpiから容量(バイト...
-
C++ Builderで文字列をバイトに...
-
64bit対応
-
ワイド文字のバイト数が取得で...
-
UCS-2の一覧表が欲しい
-
3バイト文字(UTF-8)をprintfで...
-
VBAでShift-JISのURLエンコード
-
機種依存文字をチェックしたい。
-
ビットスワップとバイトスワッ...
-
VBSでテキストファイルの文字列...
おすすめ情報