dicom.h
説明を見る。
1 //
2 // Copyright (c) 2003-2011, MIST Project, Nagoya University
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without modification,
6 // are permitted provided that the following conditions are met:
7 //
8 // 1. Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 //
11 // 2. Redistributions in binary form must reproduce the above copyright notice,
12 // this list of conditions and the following disclaimer in the documentation
13 // and/or other materials provided with the distribution.
14 //
15 // 3. Neither the name of the Nagoya University nor the names of its contributors
16 // may be used to endorse or promote products derived from this software
17 // without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
20 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
26 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 //
28 
33 #ifndef __INCLUDE_MIST_DICOM__
34 #define __INCLUDE_MIST_DICOM__
35 
36 
37 #ifndef __INCLUDE_MIST_CONF_H__
38 #include "../config/mist_conf.h"
39 #endif
40 
41 #ifndef __INCLUDE_MIST_H__
42 #include "../mist.h"
43 #endif
44 
45 #ifndef __INCLUDE_MIST_ENDIAN__
46 #include "../config/endian.h"
47 #endif
48 
49 #ifndef __INCLUDE_MIST_LIMITS__
50 #include "../limits.h"
51 #endif
52 
53 // カラー画像の設定を読み込む
54 #ifndef __INCLUDE_MIST_COLOR_H__
55 #include "../config/color.h"
56 #endif
57 
58 #ifndef __INCLUDE_MIST_DICOM_TAG__
59 #include "./dicom_tag.h"
60 #endif
61 
62 #ifndef __INCLUDE_MIST_DICOM_INFO__
63 #include "./dicom_info.h"
64 #endif
65 
66 #ifndef __INCLUDE_MIST_SINGLETON__
67 #include "../singleton.h"
68 #endif
69 
70 // mist名前空間の始まり
72 
73 
76 
84 
85 
86 // 次のマクロを定義すると,コンソール上に読み込んだタグ情報を表示する
87 // #define __SHOW_DICOM_TAG__
88 // #define __SHOW_DICOM_UNKNOWN_TAG__
89 // #define __SHOW_DICOM_ZEROBYTE_TAG__
90 // #define __CHECK_TAG_FORMAT__
91 
92 // 次のマクロを定義すると,認識不能なタグは排除する
93 // #define __EXCLUDE_DICOM_UNKNOWN_TAG__
94 // #define __EXCLUDE_DICOM_ZEROBYTE_TAG__
95 
96 
98 namespace dicom
99 {
109  inline unsigned char *check_dicom_file( unsigned char *p, unsigned char *e )
110  {
111  if( p == NULL || p + 4 >= e )
112  {
113  return( NULL );
114  }
115 
116  char *dicm = reinterpret_cast< char * >( p ) + 128;
117  if( dicm[ 0 ] == 'D' && dicm[ 1 ] == 'I' && dicm[ 2 ] == 'C' && dicm[ 3 ] == 'M' )
118  {
119  if( p + 4 + 128 >= e )
120  {
121  return( NULL );
122  }
123  else
124  {
125  return( p + 128 + 4 );
126  }
127  }
128 
129  // DICOM用のプリアンブルが存在しないため,先頭のポインタを返す
130  return( p );
131  }
132 
133 
142  inline bool is_sequence_separate_tag( const unsigned char *p, const unsigned char *e )
143  {
144  if( p + 4 > e )
145  {
146  return( false );
147  }
148  return( p[ 0 ] == 0xfe && p[ 1 ] == 0xff && p[ 2 ] == 0x00 && p[ 3 ] == 0xe0 );
149  }
150 
159  inline bool is_sequence_element_end( const unsigned char *p, const unsigned char *e )
160  {
161  if( p + 8 > e )
162  {
163  return( false );
164  }
165  return( p[ 0 ] == 0xfe && p[ 1 ] == 0xff && p[ 2 ] == 0x0d && p[ 3 ] == 0xe0 && p[ 4 ] == 0x00 && p[ 5 ] == 0x00 && p[ 6 ] == 0x00 && p[ 7 ] == 0x00 );
166  }
167 
176  inline bool is_sequence_tag_end( const unsigned char *p, const unsigned char *e )
177  {
178  if( p + 8 > e )
179  {
180  return( false );
181  }
182  return( p[ 0 ] == 0xfe && p[ 1 ] == 0xff && p[ 2 ] == 0xdd && p[ 3 ] == 0xe0 && p[ 4 ] == 0x00 && p[ 5 ] == 0x00 && p[ 6 ] == 0x00 && p[ 7 ] == 0x00 );
183  }
184 
185  template < bool _IS_SIGNED_ >
186  struct __check_num_bytes_function__
187  {
188  template < class T >
189  static ptrdiff_t check( const T &nbytes )
190  {
191  if( nbytes == type_limits< T >::maximum( ) )
192  {
193  return( -1 );
194  }
195  else
196  {
197  return( nbytes );
198  }
199  }
200  };
201 
202  template < >
203  struct __check_num_bytes_function__< true >
204  {
205  template < class T >
206  static ptrdiff_t check( const T &nbytes )
207  {
208  return( nbytes );
209  }
210  };
211 
212  template < class T >
213  inline ptrdiff_t __check_num_bytes__( const T &nbytes )
214  {
215  return( __check_num_bytes_function__< std::numeric_limits< T >::is_signed >::check( nbytes ) );
216  }
217 
233  inline unsigned char *read_dicom_tag( unsigned char *p, unsigned char *e, dicom_tag &tag, difference_type &numBytes, bool from_little_endian = true )
234  {
235  if( p == NULL || p + 8 > e )
236  {
237  numBytes = -1;
238  return( e );
239  }
240 
241  unsigned char *data = p;
242 
243  unsigned short group = to_current_endian( byte_array< unsigned short >( data ), from_little_endian ).get_value( );
244  unsigned short element = to_current_endian( byte_array< unsigned short >( data + 2 ), from_little_endian ).get_value( );
245 
246  data += 4;
247 
248  char VR[ 3 ] = { static_cast< char >( data[ 0 ] ), static_cast< char >( data[ 1 ] ), '\0' };
249  dicom_vr vr = get_dicom_vr( VR );
250 
251  difference_type num_bytes = 0;
253  tag = dicom_table.get_tag( group, element, vr );
254 
255  if( tag.tag != 0xffffffff )
256  {
257  //タグがテーブルに登録されている場合
258  if( tag.vr == vr )
259  {
260  // 明示的VR
261  switch( vr )
262  {
263  case OB:
264  case OW:
265  case UN:
266  case SQ:
267  case OF:
268  case UT:
269  data += 4;
270  num_bytes = __check_num_bytes__( to_current_endian( byte_array< unsigned int >( data ), from_little_endian ).get_value( ) );
271  break;
272 
273  default:
274  num_bytes = __check_num_bytes__( to_current_endian( byte_array< unsigned short >( data + 2 ), from_little_endian ).get_value( ) );
275  break;
276  }
277  }
278  else if( vr != UNKNOWN )
279  {
280  // 明示的VRであるが,辞書と異なる場合
281  tag.vr = vr;
282  switch( vr )
283  {
284  case OB:
285  case OW:
286  case UN:
287  case SQ:
288  case OF:
289  case UT:
290  data += 4;
291  num_bytes = __check_num_bytes__( to_current_endian( byte_array< unsigned int >( data ), from_little_endian ).get_value( ) );
292  break;
293 
294  default:
295  num_bytes = __check_num_bytes__( to_current_endian( byte_array< unsigned short >( data + 2 ), from_little_endian ).get_value( ) );
296  }
297  }
298  else
299  {
300  // 暗示的VR
301  num_bytes = __check_num_bytes__( to_current_endian( byte_array< unsigned int >( data ), from_little_endian ).get_value( ) );
302  }
303 
304  numBytes = num_bytes;
305  return( reinterpret_cast< unsigned char * >( data + 4 ) );
306  }
307  else
308  {
309  //タグがテーブルに登録されていない場合はデータスキップ
310  switch( vr )
311  {
312  case AE:
313  case AS:
314  case AT:
315  case CS:
316  case DA:
317  case DS:
318  case DT:
319  case FL:
320  case FD:
321  case IS:
322  case LO:
323  case LT:
324  case PN:
325  case SH:
326  case SL:
327  case SS:
328  case ST:
329  case TM:
330  case UI:
331  case UL:
332  case US:
333  num_bytes = __check_num_bytes__( to_current_endian( byte_array< unsigned short >( data + 2 ), from_little_endian ).get_value( ) );
334  break;
335 
336  case OB:
337  case OW:
338  case UN:
339  case SQ:
340  case OF:
341  case UT:
342  data += 4;
343  num_bytes = __check_num_bytes__( to_current_endian( byte_array< unsigned int >( data ), from_little_endian ).get_value( ) );
344  break;
345 
346  default:
347  num_bytes = __check_num_bytes__( to_current_endian( byte_array< unsigned int >( data ), from_little_endian ).get_value( ) );
348  break;
349  }
350 
351 #if defined( __SHOW_DICOM_ZEROBYTE_TAG__ ) && defined( __SHOW_DICOM_UNKNOWN_TAG__ )
352  printf( "( %04x, %04x, %s, % 8d ) = Unknown Tags!!\n", group, element, VR, num_bytes );
353 #endif
354  if( vr == SQ )
355  {
356  // 不明なタグだけどシーケンスタグということを確認
357  tag = dicom_tag( construct_dicom_tag( group, element ), SQ, -1, "UNKNOWN" );
358  numBytes = num_bytes;
359  return( data + 4 );
360  }
361  else if( data + 4 + 4 <= e && data[ 0 ] == 0xff && data[ 1 ] == 0xff && data[ 2 ] == 0xff && data[ 3 ] == 0xff && is_sequence_separate_tag( data + 4, e ) )
362  {
363  // 不明なタグだけどシーケンスタグの可能性がある場合ということを確認
364  tag = dicom_tag( construct_dicom_tag( group, element ), SQ, -1, "UNKNOWN" );
365  numBytes = num_bytes;
366  return( data + 4 );
367  }
368  else if( data + 4 + num_bytes <= e )
369  {
370  if( element == 0x0000 )
371  {
372  // Group Length タグ
373  char str[ 50 ];
374  sprintf( str, "Group %04d Length", group );
375  tag = dicom_tag( construct_dicom_tag( group, element ), UL, 1, str );
376  numBytes = num_bytes;
377  return( reinterpret_cast< unsigned char * >( data + 4 ) );
378  }
379  else
380  {
381  tag = dicom_tag( construct_dicom_tag( group, element ), vr, -1, "UNKNOWN" );
382  numBytes = num_bytes;
383  return( reinterpret_cast< unsigned char * >( data + 4 ) );
384  }
385  }
386  else
387  {
388  numBytes = -2;
389  return( p );
390  }
391  }
392  }
393 
394 
395 
405  inline bool process_dicom_tag( const dicom_tag &tag, unsigned char *byte, difference_type num_bytes, bool from_little_endian = true )
406  {
407  switch( tag.vr )
408  {
409  case AE:
410  // Application Entity
411  // 「16バイト以下」
412  // 先頭と末尾にスペース(20H)を持つ可能性のある文字列
413  // 文字列の長さを偶数に保つために,末尾にスペースを挿入する必要あり
414  // スペースは特に意味なし
415  if( tag.vm != -1 && num_bytes > 16 * tag.vm )
416  {
417  return( false );
418  }
419  break;
420 
421  case AS:
422  // 年齢を表す文字列
423  // 「4バイト固定」
424  // 文字列の書式は nnnD, nnnW, nnnM, nnnY のいずれか
425  // 使用可能な値は,0-9, D, W, M, Y のいずれか
426  //  D: nnn → 日
427  //  W: nnn → 週
428  //  M: nnn → 月
429  //  Y: nnn → 年
430  //
431  // 例: "018M" は生後18ヶ月を表す
432  if( tag.vm != -1 && ( num_bytes % 4 != 0 || (int)( num_bytes / 4 ) > tag.vm ) )
433  {
434  return( false );
435  }
436  break;
437 
438  case AT:
439  // Attribute Tag
440  // 「4バイト固定」
441  // 2つの16ビット符号なし整数の並び
442  //
443  // 例: (0018,00FF) のタグは,4バイトの並びで表現され,エンディアンの違いにより以下のように符号化される
444  // リトルエンディアン転送構文 → 18H, 00H, FFH, 00H
445  // ビッグエンディアン転送構文 → 00H, 18H, 00H, FFH
446  if( tag.vm != -1 && ( num_bytes % 4 != 0 || (int)( num_bytes / 4 ) > tag.vm ) )
447  {
448  return( false );
449  }
450 
451  if( _is_little_endian_( ) != from_little_endian )
452  {
453  for( long i = 0 ; i < num_bytes ; i += 4 )
454  {
455  unsigned char v0 = byte[ i + 0 ];
456  unsigned char v1 = byte[ i + 1 ];
457  unsigned char v2 = byte[ i + 2 ];
458  unsigned char v3 = byte[ i + 3 ];
459  byte[ i + 0 ] = v1;
460  byte[ i + 1 ] = v0;
461  byte[ i + 2 ] = v3;
462  byte[ i + 3 ] = v2;
463  }
464  }
465  break;
466 
467  case CS:
468  // Code String
469  // 「16バイト以下」
470  // 先頭と末尾にスペース(20H)を持つ可能性のある文字列
471  // 文字列の長さを偶数に保つために,末尾にスペースを挿入する必要あり
472  // スペースは特に意味なし
473  // 大文字の A-Z, 0-9, アンダーバー(_)のみを使用可能.
474  if( tag.vm != -1 && num_bytes > 16 * tag.vm )
475  {
476  return( false );
477  }
478  break;
479 
480  case DA:
481  // 日付を表す文字列
482  // 「8バイト固定」ただし,DICOM2.0では「10バイト固定」のためどちらも扱えるようにする必要あり
483  // yyyymmdd もしくは yyyy.mm.dd の書式で日付を符号化した文字列
484  // これは,yyyy 年 mm 月 dd 日を表す
485  // 0-9, ピリオド(.)のみを使用可能.
486  //
487  // 例:19930822 は 1993 年 8 月 22 日を表す
488  if( tag.vm != -1 && ( num_bytes % 8 != 0 || (int)( num_bytes / 8 ) > tag.vm ) && ( num_bytes % 10 != 0 || (int)( num_bytes / 10 ) > tag.vm ) )
489  {
490  return( false );
491  }
492  break;
493 
494  case DS:
495  // 10進数を表す文字列
496  // 「16バイト以下」
497  // 固定小数点もしくは浮動小数点数を表現する文字列であり,先頭と末尾にスペース(20H)を持つ可能性のある文字列
498  // 固定小数点数の場合は 0-9 に加え,先頭に + もしくは -,さらに小数点を示す任意の . を含む
499  // 浮動小数点数は,ANSI X3.9 に従い,指数を示す E もしくは e を含む
500  // 文字列の長さを偶数に保つために,末尾にスペースを挿入する必要あり
501  if( tag.vm != -1 && num_bytes > 16 * tag.vm )
502  {
503  return( false );
504  }
505  break;
506 
507  case DT:
508  // 日時を表す文字列
509  // 「26バイト以下」
510  // YYYYMMDDHHMMSS.FFFFFF&ZZZZ の書式に従って日時を表し,先頭と末尾にスペース(20H)を持つ可能性のある文字列
511  // これは,YYYY 年 MM 月 DD 日 HH 時 MM 分 SS.FFFFFF 秒(FFFFFFは病の小数部分)を表す
512  // また,& は + もしくは - のどちらかであり,ZZZZ は時間と分のオフセットを表す
513  // 文字列の長さを偶数に保つために,末尾にスペースを挿入する必要あり
514  if( tag.vm != -1 && num_bytes > 26 * tag.vm )
515  {
516  return( false );
517  }
518  break;
519 
520  case FL:
521  // 単精度浮動小数
522  // 「4バイト固定」
523  if( tag.vm != -1 && ( num_bytes % 4 != 0 || (int)( num_bytes / 4 ) > tag.vm ) )
524  {
525  return( false );
526  }
527 
528  {
529  byte_array< float > tmp = to_current_endian( byte_array< float >( byte ), from_little_endian );
530  byte[ 0 ] = tmp[ 0 ];
531  byte[ 1 ] = tmp[ 1 ];
532  byte[ 2 ] = tmp[ 2 ];
533  byte[ 3 ] = tmp[ 3 ];
534  }
535  break;
536 
537  case FD:
538  // 倍精度浮動小数
539  // 「8バイト固定」
540  if( tag.vm != -1 && ( num_bytes % 8 != 0 || (int)( num_bytes / 8 ) > tag.vm ) )
541  {
542  return( false );
543  }
544 
545  {
546  byte_array< double > tmp = to_current_endian( byte_array< double >( byte ), from_little_endian );
547  byte[ 0 ] = tmp[ 0 ];
548  byte[ 1 ] = tmp[ 1 ];
549  byte[ 2 ] = tmp[ 2 ];
550  byte[ 3 ] = tmp[ 3 ];
551  byte[ 4 ] = tmp[ 4 ];
552  byte[ 5 ] = tmp[ 5 ];
553  byte[ 6 ] = tmp[ 6 ];
554  byte[ 7 ] = tmp[ 7 ];
555  }
556  break;
557 
558  case IS:
559  // 整数を表す文字列
560  // 「16バイト以下」
561  // 10 を底とする整数(10進数)を表わす文字列で,先頭に + もしくは - を含んでも良く,先頭と末尾にスペース(20H)を持つ可能性のある文字列
562  // 文字列の長さを偶数に保つために,末尾にスペースを挿入する必要あり
563  // -2^31 ≦ n ≦ (2^31-1) の範囲を表現することが可能.
564  if( tag.vm != -1 && num_bytes > 16 * tag.vm )
565  {
566  return( false );
567  }
568  break;
569 
570  case LO:
571  // 長文字列
572  // 「64バイト以下」
573  // 先頭と末尾にスペース(20H)を持つ文字列
574  // 文字列の長さを偶数に保つために,末尾にスペースを挿入する必要あり
575  // 制御文字列は含まない
576  if( tag.vm != -1 && num_bytes > 64 * tag.vm )
577  {
578  return( false );
579  }
580  break;
581 
582  case LT:
583  // 長テキスト文字列
584  // 「10240バイト以下」
585  // 末尾にスペース(20H)を持つ可能性のある文字列
586  // 文字列の長さを偶数に保つために,末尾にスペースを挿入する必要あり
587  // 制御文字コードを含む
588  if( tag.vm != -1 && num_bytes > 10240 * tag.vm )
589  {
590  return( false );
591  }
592  break;
593 
594  case OB:
595  // バイト列
596  // 「無制限」
597  // 転送構文のエンディアン形式に依存しないデータ列
598  // バイト列の長さを偶数に保つために,末尾にNULL値(00H)で埋められる可能性あり
599  break;
600 
601  case OF:
602  // 4バイト浮動小数のバイト列の並びワード列
603  // 「無制限」
604  // 転送構文のエンディアン形式に依存して,バイトの並びが変化するデータ列
605  if( _is_little_endian_( ) != from_little_endian )
606  {
607  for( long i = 0 ; i < num_bytes ; i += 4 )
608  {
609  unsigned char v1 = byte[ i + 0 ];
610  unsigned char v2 = byte[ i + 1 ];
611  unsigned char v3 = byte[ i + 2 ];
612  unsigned char v4 = byte[ i + 3 ];
613  byte[ i + 0 ] = v4;
614  byte[ i + 1 ] = v3;
615  byte[ i + 2 ] = v2;
616  byte[ i + 3 ] = v1;
617  }
618  }
619  break;
620 
621  case OW:
622  // ワード列
623  // 「無制限」
624  // 転送構文のエンディアン形式に依存して,バイトの並びが変化するデータ列
625  if( _is_little_endian_( ) != from_little_endian )
626  {
627  for( long i = 0 ; i < num_bytes ; i += 2 )
628  {
629  unsigned char tmp = byte[ i ];
630  byte[ i ] = byte[ i + 1 ];
631  byte[ i + 1 ] = tmp;
632  }
633  }
634  break;
635 
636  case PN:
637  // 患者名を表す文字列
638  // 「64バイト以下」
639  // 制御文字列を一切含まず,末尾にスペース(20H)を持つ可能性のある文字列
640  // 文字列の長さを偶数に保つために,末尾にスペースを挿入する必要あり
641  // 姓と名の区切りには,^ (5EH) が用いられる
642  if( tag.vm != -1 && num_bytes > 64 * tag.vm )
643  {
644  return( false );
645  }
646  break;
647 
648  case SH:
649  // 短文字列
650  // 「16バイト以下」
651  // 先頭と末尾にスペース(20H)を持つ文字列
652  // 文字列の長さを偶数に保つために,末尾にスペースを挿入する必要あり
653  // 制御文字列は含まない
654  if( tag.vm != -1 && num_bytes > 16 * tag.vm )
655  {
656  return( false );
657  }
658  break;
659 
660  case SL:
661  // 符号付き4バイト整数
662  // 「4バイト固定」
663  if( tag.vm != -1 && ( num_bytes % 4 != 0 || (int)( num_bytes / 4 ) > tag.vm ) )
664  {
665  return( false );
666  }
667 
668  {
669  byte_array< signed int > tmp = to_current_endian( byte_array< signed int >( byte ), from_little_endian );
670  byte[ 0 ] = tmp[ 0 ];
671  byte[ 1 ] = tmp[ 1 ];
672  byte[ 2 ] = tmp[ 2 ];
673  byte[ 3 ] = tmp[ 3 ];
674  }
675  break;
676 
677  case SQ:
678  // シーケンスを表すタグ
679  // 「無制限」
680  // DICOMタグのシーケンスを格納するタグ
681  break;
682 
683  case SS:
684  // 符号付き2バイト整数
685  // 「2バイト固定」
686  if( tag.vm != -1 && ( num_bytes % 2 != 0 || (int)( num_bytes / 2 ) > tag.vm ) )
687  {
688  return( false );
689  }
690 
691  {
692  byte_array< signed short > tmp = to_current_endian( byte_array< signed short >( byte ), from_little_endian );
693  byte[ 0 ] = tmp[ 0 ];
694  byte[ 1 ] = tmp[ 1 ];
695  }
696  break;
697 
698  case ST:
699  // 短テキスト文字列
700  // 末尾にスペース(20H)を持つ可能性のある文字列
701  // 文字列の長さを偶数に保つために,末尾にスペースを挿入する必要あり
702  // 制御文字コードを含む
703  if( tag.vm != -1 && num_bytes > 1024 * tag.vm )
704  {
705  return( false );
706  }
707  break;
708 
709  case TM:
710  // 時刻を表す文字列
711  // 「16バイト以下」
712  // hhmmss.frac の書式で時刻を符号化した文字列
713  // これは,hh 時 mm 分 ss 秒 (frac は1億分の1秒単位) を表す
714  // 24時間形式の時刻表記が用いられ,深夜24時は0時として扱われる
715  // 文字列の長さを偶数に保つために,末尾にスペースを挿入する必要あり
716  if( tag.vm != -1 && num_bytes > 16 * tag.vm )
717  {
718  return( false );
719  }
720  break;
721 
722  case UI:
723  // UIDを表す文字列
724  // 「64バイト以下」
725  // 文字列の長さを偶数に保つために,末尾にNULL (00H) を挿入する必要あり
726  if( tag.vm != -1 && num_bytes > 64 * tag.vm )
727  {
728  return( false );
729  }
730  break;
731 
732  case UL:
733  // 符号無し4バイト整数
734  // 「4バイト固定」
735  if( tag.vm != -1 && ( num_bytes % 4 != 0 || (int)( num_bytes / 4 ) > tag.vm ) )
736  {
737  return( false );
738  }
739 
740  {
741  byte_array< unsigned int > tmp = to_current_endian( byte_array< unsigned int >( byte ), from_little_endian );
742  byte[ 0 ] = tmp[ 0 ];
743  byte[ 1 ] = tmp[ 1 ];
744  byte[ 2 ] = tmp[ 2 ];
745  byte[ 3 ] = tmp[ 3 ];
746  }
747  break;
748 
749  case UN:
750  // 不明なタグ
751  break;
752 
753  case US:
754  // 符号無し2バイト整数
755  // 「2バイト固定」
756  if( tag.vm != -1 && ( num_bytes % 2 != 0 || (int)( num_bytes / 2 ) > tag.vm ) )
757  {
758  return( false );
759  }
760 
761  {
763  byte[ 0 ] = tmp[ 0 ];
764  byte[ 1 ] = tmp[ 1 ];
765  }
766  break;
767 
768  case UT:
769  // 無制限のテキスト文字列
770  // 末尾にスペース(20H)を持つ可能性のある文字列
771  // 文字列の長さを偶数に保つために,末尾にスペースを挿入する必要あり
772  // 制御文字コードを含む
773  break;
774 
775  default:
776  // VRがわかんないからとりあえず良しとする
777  break;
778  }
779 
780  return( true );
781  }
782 
783 
796  inline unsigned char *process_dicom_tag( dicom_tag_container &dicm, unsigned char *pointer, unsigned char *end_pointer, bool from_little_endian = true, bool is_in_sequence_tag = false )
797  {
798  difference_type numBytes = 0;
799  dicom_tag tag;
800 
801  pointer = read_dicom_tag( pointer, end_pointer, tag, numBytes, from_little_endian );
802 
803  if( tag.vr == SQ )
804  {
805  unsigned char *sp = pointer;
806  if( numBytes != -1 && pointer + numBytes <= end_pointer )
807  {
808  pointer += numBytes;
809  }
810  else
811  {
812  unsigned char *ep = numBytes == -1 ? end_pointer : pointer + numBytes;
813  if( ep > end_pointer )
814  {
815  // 認識不能なシーケンスタグ発見
816  return( NULL );
817  }
818  while( pointer + 8 <= ep )
819  {
820  if( is_sequence_tag_end( pointer, end_pointer ) )
821  {
822  pointer += 8;
823  break;
824  }
825  else
826  {
827  if( !is_sequence_separate_tag( pointer, ep ) )
828  {
829  return( NULL );
830  }
831 
832  pointer += 4;
833  numBytes = __check_num_bytes__( to_current_endian( byte_array< unsigned int >( pointer ), from_little_endian ).get_value( ) );
834  pointer += 4;
835 
836  unsigned char *epp = numBytes == -1 ? ep : pointer + numBytes;
837  if( epp > ep )
838  {
839  return( NULL );
840  }
841 
842  while( pointer + 8 <= epp )
843  {
844  if( is_sequence_element_end( pointer, ep ) )
845  {
846  pointer += 8;
847  break;
848  }
849  else
850  {
851  pointer = process_dicom_tag( dicm, pointer, ep, from_little_endian, true );
852  if( pointer == NULL )
853  {
854  return( NULL );
855  }
856  }
857  }
858  }
859  }
860  }
861 
862  if( !is_in_sequence_tag )
863  {
865  ite = dicm.append( dicom_element( tag, sp, pointer - sp ) );
866 
867 #ifdef __SHOW_DICOM_TAG__
868  if( ite != dicm.end( ) )
869  {
870  ite->second.show_tag( );
871  }
872 #endif
873  }
874  }
875  else if( numBytes < -1 )
876  {
877  return( NULL );
878  }
879  else if( numBytes == -1 )
880  {
881  // 圧縮がかかっている可能性があるのでチェック
883  switch( tag.vr )
884  {
885  case OB:
886  case OW:
887  {
888  unsigned char *p = pointer;
889  while( p + 8 <= end_pointer )
890  {
891  if( is_sequence_tag_end( p, end_pointer ) )
892  {
893  p += 8;
894  break;
895  }
896  else
897  {
898  if( !is_sequence_separate_tag( p, end_pointer ) )
899  {
900  return( NULL );
901  }
902 
903  p += 4;
904  numBytes = __check_num_bytes__( to_current_endian( byte_array< unsigned int >( p ), from_little_endian ).get_value( ) );
905  p += 4 + numBytes;
906 
907  if( numBytes < 0 || p > end_pointer )
908  {
909  return( NULL );
910  }
911  }
912  }
913  if( p <= end_pointer )
914  {
915  numBytes = p - pointer;
916  if( !is_in_sequence_tag )
917  {
918  ite = dicm.append( dicom_element( tag, pointer, numBytes ) );
919  }
920  pointer += 8;
921  }
922  else
923  {
924  // 圧縮シーケンスの終わりをうまく検出できなかった
925  return( NULL );
926  }
927  }
928  break;
929 
930  default:
931  return( NULL );
932  }
933 
934  pointer += numBytes;
935 
936 #ifdef __SHOW_DICOM_TAG__
937  if( !is_in_sequence_tag && ite != dicm.end( ) )
938  {
939  ite->second.show_tag( );
940  }
941 #endif
942  }
943  else if( numBytes > 0 )
944  {
945  // 不適切なバイト数かどうかをチェックする
946  if( pointer + numBytes > end_pointer )
947  {
948  return( NULL );
949  }
950 
951 #ifndef __CHECK_TAG_FORMAT__
952  if( !process_dicom_tag( tag, pointer, numBytes, from_little_endian ) )
953  {
954  // 規格に従っていないタグを発見したが,とりあえず無視しておく
955  std::cout << "Illegal DICOM tag is found!" << std::endl;
956  }
957 #else
958  if( !process_dicom_tag( tag, pointer, numBytes, from_little_endian ) )
959  {
960  // 処理することができないDICOMタグを発見したので終了する
961  return( NULL );
962  }
963 #endif
964  if( !is_in_sequence_tag )
965  {
967  ite = dicm.append( dicom_element( tag, pointer, numBytes ) );
968 
969 #ifdef __SHOW_DICOM_TAG__
970  if( ite != dicm.end( ) )
971  {
972  ite->second.show_tag( );
973  }
974 #endif
975  }
976 
977  pointer += numBytes;
978  }
979  else if( tag.vr != UNKNOWN && !is_in_sequence_tag )
980  {
981  // 不明なタグもリストに追加する
983  ite = dicm.append( dicom_element( tag, NULL, 0 ) );
984 
985 #if defined( __SHOW_DICOM_ZEROBYTE_TAG__ ) && defined( __SHOW_DICOM_TAG__ )
986  if( ite != dicm.end( ) )
987  {
988  ite->second.show_tag( );
989  }
990 #endif
991  }
992 
993  return( pointer );
994  }
995 
996 
1007  inline bool read_dicom_tags( dicom_tag_container &dicm, unsigned char *buff, size_type numBytes, bool is_little_endian = true )
1008  {
1009  unsigned char *pointer = buff;
1010  unsigned char *end_pointer = buff + numBytes;
1011 
1012  // DICOMデータの先頭までポインタを移動
1013  pointer = check_dicom_file( pointer, end_pointer );
1014 
1015  dicm.clear( );
1016 
1017  unsigned char *group_end_pointer = NULL;
1018  bool ret = true, from_little_endian = is_little_endian, once = true;
1019  while( pointer < end_pointer )
1020  {
1021  pointer = process_dicom_tag( dicm, pointer, end_pointer, from_little_endian );
1022  if( pointer == NULL )
1023  {
1024  ret = false;
1025  break;
1026  }
1027 
1028  if( group_end_pointer == NULL && dicm.contain( 0x0002, 0x0000 ) )
1029  {
1030  // エンディアンの判定に用いる範囲を決定する
1031  group_end_pointer = pointer + find_tag( dicm, 0x0002, 0x0000, static_cast< unsigned int >( 0 ) );
1032  }
1033 
1034  if( group_end_pointer != NULL && once && dicm.contain( 0x0002, 0x0010 ) )
1035  {
1036  // 一度しか実行されないようにする
1037  once = false;
1038 
1039  // エンディアンの形式を設定する
1040  from_little_endian = find_tag( dicm, 0x0002, 0x0010, "" ) != "1.2.840.10008.1.2.2";
1041 
1042  // ビッグエンディアンの場合は,このブロックだけリトルエンディアンで処理する
1043  if( !from_little_endian )
1044  {
1045  while( pointer < group_end_pointer )
1046  {
1047  pointer = process_dicom_tag( dicm, pointer, group_end_pointer, true );
1048  if( pointer == NULL )
1049  {
1050  pointer = end_pointer;
1051  ret = false;
1052  break;
1053  }
1054  }
1055  }
1056  }
1057  }
1058 
1059 #ifdef __SHOW_DICOM_TAG__
1060  printf( "\n\n\n" );
1061 #endif
1062 
1063  return( ret );
1064  }
1065 
1066 
1075  inline bool read_dicom_tags( dicom_tag_container &dicm, const std::string &filename )
1076  {
1077  size_type filesize;
1078  FILE *fp;
1079  if( ( fp = fopen( filename.c_str( ), "rb" ) ) == NULL )
1080  {
1081  return( false );
1082  }
1083 
1084  // ファイルサイズを取得
1085  fseek( fp, 0, SEEK_END );
1086  filesize = ftell( fp );
1087  fseek( fp, 0, SEEK_SET );
1088 
1089  unsigned char *buff = new unsigned char[ filesize + 1 ];
1090  unsigned char *pointer = buff;
1091  size_type read_size = 0;
1092  while( feof( fp ) == 0 )
1093  {
1094  read_size = fread( pointer, sizeof( unsigned char ), 1024, fp );
1095  if( read_size < 1024 )
1096  {
1097  break;
1098  }
1099  pointer += read_size;
1100  }
1101 
1102  fclose( fp );
1103 
1104  bool ret = read_dicom_tags( dicm, buff, filesize, true );
1105 
1106  delete [] buff;
1107 
1108 #ifdef __SHOW_DICOM_TAG__
1109  printf( "\n\n\n" );
1110 #endif
1111 
1112  // 適切なDICOMファイルかどうかをチェックする
1113  if( ret )
1114  {
1115  dicom_tag_container::iterator ite = dicm.begin( );
1116  for( ; ite != dicm.end( ) ; ++ite )
1117  {
1118  if( ite->second.comment != "UNKNOWN" )
1119  {
1120  return( true );
1121  }
1122  }
1123 
1124  return( false );
1125  }
1126  else
1127  {
1128  return( false );
1129  }
1130  }
1131 
1132 
1146  inline bool write_dicom_tag_explicit_vr( unsigned short group, unsigned short element, dicom_vr vr, const unsigned char *data, size_t num_bytes, FILE *fp, bool to_little_endian = true )
1147  {
1148  unsigned char ZERO[] = { 0, 0, 0, 0 };
1149  unsigned char FFFF[] = { 0xff, 0xff, 0xff, 0xff };
1150 
1151  switch( vr )
1152  {
1153  case OW:
1154  case OF:
1155  fwrite( from_current_endian( byte_array< unsigned short >( group ), to_little_endian ).get_bytes( ), 1, 2, fp );
1156  fwrite( from_current_endian( byte_array< unsigned short >( element ), to_little_endian ).get_bytes( ), 1, 2, fp );
1157  fwrite( get_dicom_vr( vr ).c_str( ), 1, 2, fp );
1158  fwrite( ZERO, 1, 2, fp );
1159 
1160  // データがシーケンスになっているかを調べ,出力方法を切り替える
1161  if( num_bytes > 4 + 8 && is_sequence_separate_tag( data, data + num_bytes ) && is_sequence_tag_end( data + num_bytes - 8, data + num_bytes ) )
1162  {
1163  fwrite( FFFF, 1, 4, fp );
1164  }
1165  else
1166  {
1167  fwrite( from_current_endian( byte_array< unsigned int >( static_cast< unsigned int >( num_bytes ) ), to_little_endian ).get_bytes( ), 1, 4, fp );
1168  }
1169 
1170  if( _is_little_endian_( ) == to_little_endian )
1171  {
1172  fwrite( data, 1, num_bytes, fp );
1173  }
1174  else
1175  {
1176  switch( vr )
1177  {
1178  case OW:
1179  if( num_bytes % 2 == 0 )
1180  {
1181  for( size_t i = 0 ; i < num_bytes ; i += 2 )
1182  {
1183  fwrite( from_current_endian( byte_array< unsigned short >( data + i ), to_little_endian ).get_bytes( ), 1, 2, fp );
1184  }
1185  }
1186  else
1187  {
1188  // データのバイト数がおかしい
1189  return( false );
1190  }
1191  break;
1192 
1193  case OF:
1194  default:
1195  if( num_bytes % 4 == 0 )
1196  {
1197  for( size_t i = 0 ; i < num_bytes ; i += 4 )
1198  {
1199  fwrite( from_current_endian( byte_array< unsigned int >( data + i ), to_little_endian ).get_bytes( ), 1, 4, fp );
1200  }
1201  }
1202  else
1203  {
1204  // データのバイト数がおかしい
1205  return( false );
1206  }
1207  break;
1208  }
1209  }
1210  break;
1211 
1212  case US:
1213  case SS:
1214  case AT:
1215  case UL:
1216  case SL:
1217  case FL:
1218  case FD:
1219  fwrite( from_current_endian( byte_array< unsigned short >( group ), to_little_endian ).get_bytes( ), 1, 2, fp );
1220  fwrite( from_current_endian( byte_array< unsigned short >( element ), to_little_endian ).get_bytes( ), 1, 2, fp );
1221  fwrite( get_dicom_vr( vr ).c_str( ), 1, 2, fp );
1222  fwrite( from_current_endian( byte_array< unsigned short >( static_cast< unsigned short >( num_bytes ) ), to_little_endian ).get_bytes( ), 1, 2, fp );
1223  if( _is_little_endian_( ) == to_little_endian )
1224  {
1225  fwrite( data, 1, num_bytes, fp );
1226  }
1227  else
1228  {
1229  switch( vr )
1230  {
1231  case US:
1232  case SS:
1233  case AT:
1234  if( num_bytes % 2 == 0 )
1235  {
1236  for( size_t i = 0 ; i < num_bytes ; i += 2 )
1237  {
1238  fwrite( from_current_endian( byte_array< unsigned short >( data + i ), to_little_endian ).get_bytes( ), 1, 2, fp );
1239  }
1240  }
1241  else
1242  {
1243  // データのバイト数がおかしい
1244  return( false );
1245  }
1246  break;
1247 
1248  case UL:
1249  case SL:
1250  case FL:
1251  if( num_bytes % 4 == 0 )
1252  {
1253  for( size_t i = 0 ; i < num_bytes ; i += 4 )
1254  {
1255  fwrite( from_current_endian( byte_array< unsigned int >( data + i ), to_little_endian ).get_bytes( ), 1, 4, fp );
1256  }
1257  }
1258  else
1259  {
1260  // データのバイト数がおかしい
1261  return( false );
1262  }
1263  break;
1264 
1265  case FD:
1266  default:
1267  if( num_bytes % 8 == 0 )
1268  {
1269  for( size_t i = 0 ; i < num_bytes ; i += 8 )
1270  {
1271  fwrite( from_current_endian( byte_array< double >( data + i ), to_little_endian ).get_bytes( ), 1, 8, fp );
1272  }
1273  }
1274  else
1275  {
1276  // データのバイト数がおかしい
1277  return( false );
1278  }
1279  break;
1280  }
1281  }
1282  break;
1283 
1284  case OB:
1285  case UN:
1286  case UT:
1287  case SQ:
1288  fwrite( from_current_endian( byte_array< unsigned short >( group ), to_little_endian ).get_bytes( ), 1, 2, fp );
1289  fwrite( from_current_endian( byte_array< unsigned short >( element ), to_little_endian ).get_bytes( ), 1, 2, fp );
1290  fwrite( get_dicom_vr( vr ).c_str( ), 1, 2, fp );
1291  fwrite( ZERO, 1, 2, fp );
1292  // データがシーケンスになっているかを調べ,出力方法を切り替える
1293  if( num_bytes > 4 + 8 && is_sequence_separate_tag( data, data + num_bytes ) && is_sequence_tag_end( data + num_bytes - 8, data + num_bytes ) )
1294  {
1295  fwrite( FFFF, 1, 4, fp );
1296  fwrite( data, 1, num_bytes, fp );
1297  }
1298  else
1299  {
1300  fwrite( from_current_endian( byte_array< unsigned int >( static_cast< unsigned int >( num_bytes ) ), to_little_endian ).get_bytes( ), 1, 4, fp );
1301  fwrite( data, 1, num_bytes, fp );
1302  }
1303  break;
1304 
1305  case UNKNOWN:
1306  fwrite( from_current_endian( byte_array< unsigned short >( group ), to_little_endian ).get_bytes( ), 1, 2, fp );
1307  fwrite( from_current_endian( byte_array< unsigned short >( element ), to_little_endian ).get_bytes( ), 1, 2, fp );
1308  fwrite( "UN", 1, 2, fp );
1309  fwrite( get_dicom_vr( vr ).c_str( ), 1, 2, fp );
1310  fwrite( from_current_endian( byte_array< unsigned int >( static_cast< unsigned int >( num_bytes ) ), to_little_endian ).get_bytes( ), 1, 4, fp );
1311  fwrite( data, 1, num_bytes, fp );
1312  break;
1313 
1314  default:
1315  fwrite( from_current_endian( byte_array< unsigned short >( group ), to_little_endian ).get_bytes( ), 1, 2, fp );
1316  fwrite( from_current_endian( byte_array< unsigned short >( element ), to_little_endian ).get_bytes( ), 1, 2, fp );
1317  fwrite( get_dicom_vr( vr ).c_str( ), 1, 2, fp );
1318  fwrite( from_current_endian( byte_array< unsigned short >( static_cast< unsigned short >( num_bytes ) ), to_little_endian ).get_bytes( ), 1, 2, fp );
1319  fwrite( data, 1, num_bytes, fp );
1320  break;
1321  }
1322 
1323  return( true );
1324  }
1325 
1326 
1327 
1341  inline bool write_dicom_tag_implicit_vr( unsigned short group, unsigned short element, dicom_vr vr, const unsigned char *data, size_t num_bytes, FILE *fp, bool to_little_endian = true )
1342  {
1343  unsigned char FFFF[] = { 0xff, 0xff, 0xff, 0xff };
1344 
1345  switch( vr )
1346  {
1347  case OB:
1348  case UN:
1349  case UT:
1350  case SQ:
1351  // データがシーケンスになっているかを調べ,出力方法を切り替える
1352  if( num_bytes > 4 + 8 && is_sequence_separate_tag( data, data + num_bytes ) && is_sequence_tag_end( data + num_bytes - 8, data + num_bytes ) )
1353  {
1354  fwrite( from_current_endian( byte_array< unsigned short >( group ), to_little_endian ).get_bytes( ), 1, 2, fp );
1355  fwrite( from_current_endian( byte_array< unsigned short >( element ), to_little_endian ).get_bytes( ), 1, 2, fp );
1356  fwrite( FFFF, 1, 4, fp );
1357  fwrite( data, 1, num_bytes, fp );
1358  }
1359  else
1360  {
1361  fwrite( from_current_endian( byte_array< unsigned short >( group ), to_little_endian ).get_bytes( ), 1, 2, fp );
1362  fwrite( from_current_endian( byte_array< unsigned short >( element ), to_little_endian ).get_bytes( ), 1, 2, fp );
1363  fwrite( from_current_endian( byte_array< unsigned int >( static_cast< unsigned int >( num_bytes ) ), to_little_endian ).get_bytes( ), 1, 4, fp );
1364  fwrite( data, 1, num_bytes, fp );
1365  }
1366  break;
1367 
1368  default:
1369  fwrite( from_current_endian( byte_array< unsigned short >( group ), to_little_endian ).get_bytes( ), 1, 2, fp );
1370  fwrite( from_current_endian( byte_array< unsigned short >( element ), to_little_endian ).get_bytes( ), 1, 2, fp );
1371  fwrite( from_current_endian( byte_array< unsigned int >( static_cast< unsigned int >( num_bytes ) ), to_little_endian ).get_bytes( ), 1, 4, fp );
1372  fwrite( data, 1, num_bytes, fp );
1373  break;
1374  }
1375 
1376  return( true );
1377  }
1378 
1388  inline bool write_dicom_tag_explicit_vr( const dicom_element &e, FILE *fp, bool to_little_endian = true )
1389  {
1390  return( write_dicom_tag_explicit_vr( e.get_group( ), e.get_element( ), e.vr, e.data, e.num_bytes, fp, to_little_endian ) );
1391  }
1392 
1402  inline bool write_dicom_tag_implicit_vr( const dicom_element &e, FILE *fp, bool to_little_endian = true )
1403  {
1404  return( write_dicom_tag_implicit_vr( e.get_group( ), e.get_element( ), e.vr, e.data, e.num_bytes, fp, to_little_endian ) );
1405  }
1406 
1407 
1415  inline size_t get_write_dicom_tag_size( const dicom_element &e, bool implicitVR )
1416  {
1417  if( implicitVR )
1418  {
1419  return( 8 + e.num_bytes );
1420  }
1421  else
1422  {
1423  size_t num_bytes = 0;
1424 
1425  switch( e.vr )
1426  {
1427  case OW:
1428  case OF:
1429  case OB:
1430  case UN:
1431  case SQ:
1432  case UT:
1433  case UNKNOWN:
1434  num_bytes = 12 + e.num_bytes;
1435  break;
1436 
1437  case US:
1438  case SS:
1439  case AT:
1440  case UL:
1441  case SL:
1442  case FL:
1443  case FD:
1444  default:
1445  num_bytes = 8 + e.num_bytes;
1446  break;
1447  }
1448 
1449  return( num_bytes );
1450  }
1451  }
1452 
1453 
1459  inline void compute_group_length( dicom_tag_container &dicm, bool implicitVR )
1460  {
1461  std::map< unsigned short, size_t > group_length;
1463  for( ite = dicm.begin( ) ; ite != dicm.end( ) ; ++ite )
1464  {
1465  const dicom_element &e = ite->second;
1466  if( e.enable )
1467  {
1468  unsigned short group = e.get_group( );
1469  unsigned short element = e.get_element( );
1470  // Group 0002 だけは Explicit VR Little Endian Transfer Syntax でエンコードする必要あり
1471  size_t num_bytes = get_write_dicom_tag_size( e, group == 0x0002 ? false : implicitVR );
1472 
1473  if( element != 0x0000 )
1474  {
1475  // Group Length のタグはスキップする
1476  if( group_length.find( group ) != group_length.end( ) )
1477  {
1478  group_length[ group ] += num_bytes;
1479  }
1480  else
1481  {
1482  group_length[ group ] = num_bytes;
1483  }
1484  }
1485  }
1486  }
1487 
1488  for( ite = dicm.begin( ) ; ite != dicm.end( ) ; ++ite )
1489  {
1490  dicom_element &e = ite->second;
1491  unsigned short group = e.get_group( );
1492  unsigned short element = e.get_element( );
1493 
1494  if( element == 0x0000 )
1495  {
1496  // Group Length のタグにデータを設定する
1497  size_t num_bytes = group_length[ group ];
1498 
1499  switch( e.num_bytes )
1500  {
1501  case 2:
1502  {
1503  byte_array< unsigned short > b( static_cast< unsigned short >( num_bytes ) );
1504  memcpy( e.data, b.get_bytes( ), e.num_bytes );
1505  }
1506  break;
1507 
1508  case 4:
1509  default:
1510  {
1511  byte_array< unsigned int > b( static_cast< unsigned int >( num_bytes ) );
1512  memcpy( e.data, b.get_bytes( ), e.num_bytes );
1513  }
1514  break;
1515  }
1516  }
1517  }
1518  }
1519 
1520 
1529  inline bool write_dicom_tags( const dicom_tag_container &dicom, const std::string &filename )
1530  {
1531  FILE *fp;
1532  if( ( fp = fopen( filename.c_str( ), "wb" ) ) == NULL )
1533  {
1534  return( false );
1535  }
1536 
1537  // DICOMのプリアンブルのタグが存在するかどうかを調べる
1538  bool hasPreamble = false;
1539  {
1540  unsigned short elements[] = { 0x0001, 0x0002, 0x0003, 0x0010, 0x0012, 0x0013, 0x0016, 0x0100, 0x0102 };
1541  size_t num = sizeof( elements ) / sizeof( unsigned short );
1542  for( size_t i = 0 ; i < num ; i++ )
1543  {
1544  if( dicom.contain( 0x0002, elements[ i ] ) )
1545  {
1546  // データが存在するのでプリアンブルを埋め込む
1547  hasPreamble = true;
1548  break;
1549  }
1550  }
1551  }
1552 
1553  dicom_tag_container dicm( dicom );
1554  bool implicitVR = true;
1555 
1556  if( hasPreamble )
1557  {
1558  // DICOMのヘッダ情報を書き込む
1559  unsigned char ZERO[ 128 ];
1560  unsigned char DICM[ 4 ];
1561  memset( ZERO, 0, sizeof( unsigned char ) * 128 );
1562  DICM[ 0 ] = 'D';
1563  DICM[ 1 ] = 'I';
1564  DICM[ 2 ] = 'C';
1565  DICM[ 3 ] = 'M';
1566  fwrite( ZERO, sizeof( unsigned char ), 128, fp );
1567  fwrite( DICM, sizeof( unsigned char ), 4, fp );
1568 
1569 
1570  // 明示的VRの指定がない場合は追加する
1571  if( !dicm.contain( 0x0002, 0x0010 ) )
1572  {
1573  // Transfer Syntax UID が存在しない場合は,「Implicit VR Little Endian」を指定する
1574  std::string syntax = "1.2.840.10008.1.2";
1575  dicm.append( dicom_element( 0x0002, 0x0010, reinterpret_cast< const unsigned char * >( syntax.c_str( ) ), syntax.length( ) ) );
1576  }
1577  else if( find_tag( dicm, 0x0002, 0x0010, "" ) == "1.2.840.10008.1.2.1" )
1578  {
1579  // 「Explicit VR Little Endian」が指定されている場合は,「Explicit VR Little Endian」に変更する
1580  implicitVR = false;
1581  }
1582  else if( find_tag( dicm, 0x0002, 0x0010, "" ) == "1.2.840.10008.1.2.2" )
1583  {
1584  // 「Implicit VR Big Endian」が指定されている場合は,「Explicit VR Little Endian」に変更する
1585  implicitVR = false;
1586  std::string syntax = "1.2.840.10008.1.2.1";
1587  dicm( 0x0002, 0x0010 ).copy( reinterpret_cast< const unsigned char * >( syntax.c_str( ) ), syntax.length( ) );
1588  }
1589  else if( get_compress_type( find_tag( dicm, 0x0002, 0x0010, "" ) ) != RAW )
1590  {
1591  // 圧縮されている場合は「Explicit VR Little Endian」に変更する
1592  implicitVR = false;
1593  }
1594 
1595  // Group 0002 の長さが挿入されていない場合は挿入する
1596  if( !dicm.contain( 0x0002, 0x0000 ) )
1597  {
1598  byte_array< unsigned int > b( static_cast< unsigned int >( 0 ) );
1599  dicm.append( dicom_element( 0x0002, 0x0000, b.get_bytes( ), b.length( ) ) );
1600  }
1601  }
1602 
1603  // DICOM のタグが使用に準拠するようにする
1604  dicm.update( );
1605 
1606  // Group Length を正しく設定する
1607  compute_group_length( dicm, implicitVR );
1608 
1609  // VRに応じて出力する
1610  dicom_tag_container::const_iterator ite = dicm.begin( );
1611  for( ; ite != dicm.end( ) ; ++ite )
1612  {
1613  if( ite->second.enable )
1614  {
1615  // Group 0002 だけは Explicit VR Little Endian Transfer Syntax でエンコードする必要あり
1616  if( implicitVR && ite->second.get_group( ) != 0x0002 )
1617  {
1618  write_dicom_tag_implicit_vr( ite->second, fp, true );
1619  }
1620  else
1621  {
1622  write_dicom_tag_explicit_vr( ite->second, fp, true );
1623  }
1624  }
1625  }
1626 
1627  fclose( fp );
1628 
1629  return( true );
1630  }
1631 }
1632 
1633 
1634 
1635 
1646 template < class T, class Allocator >
1647 bool read_dicom( array2< T, Allocator > &image, const std::string &filename )
1648 {
1649  typedef typename array2< T, Allocator >::value_type value_type;
1650  typedef typename array2< T, Allocator >::size_type size_type;
1651  typedef _pixel_converter_< T > pixel_converter;
1652  typedef typename pixel_converter::color_type color_type;
1653 
1655  dicom::dicom_info info;
1656 
1657  if( !dicom::read_dicom_tags( dicm, filename ) )
1658  {
1659  return( false );
1660  }
1661 
1662  dicom::get_dicom_info( dicm, info );
1663 
1664  double window_level = info.window_center;
1665  double window_width = info.window_width;
1666 
1667  if( dicm.contain( 0x7fe0, 0x0010 ) )
1668  {
1669  switch( info.compression_type )
1670  {
1671  case dicom::JPEG:
1672  case dicom::JPEGLS:
1673  case dicom::JPEG2000:
1674  // 今のところ未サポート
1675  return( false );
1676 
1677  default:
1678  break;
1679  }
1680 
1681  dicom::dicom_element element = dicm( 0x7fe0, 0x0010 );
1682  if( !dicom::decode( element, info ) )
1683  {
1684  // 圧縮の解凍もしくはデータがおかしい
1685  return( false );
1686  }
1687 
1688  image.resize( info.cols, info.rows );
1689  image.reso1( info.pixel_spacing_x );
1690  image.reso2( info.pixel_spacing_y );
1691 
1692  switch( info.photometric_type )
1693  {
1694  case dicom::RGB:
1695  if( info.compression_type == dicom::RLE )
1696  {
1697  // RLEで圧縮する際は,色ごとに圧縮されるみたい
1698  for( size_type j = 0 ; j < image.height( ) ; j++ )
1699  {
1700  unsigned char *r = element.data + j * info.cols;
1701  unsigned char *g = element.data + j * info.cols + info.cols * info.rows;
1702  unsigned char *b = element.data + j * info.cols + info.cols * info.rows * 2;
1703  for( size_type i = 0 ; i < image.width( ) ; i++ )
1704  {
1705  image( i, j ) = pixel_converter::convert_to( r[ i ], g[ i ], b[ i ] );
1706  }
1707  }
1708  }
1709  else
1710  {
1711  for( size_type j = 0 ; j < image.height( ) ; j++ )
1712  {
1713  unsigned char *data = element.data + j * image.width( ) * 3;
1714  for( size_type i = 0 ; i < image.width( ) ; i++ )
1715  {
1716  size_type ii = i * 3;
1717  image( i, j ) = pixel_converter::convert_to( data[ ii + 2 ], data[ ii + 1 ], data[ ii + 0 ] );
1718  }
1719  }
1720  }
1721  break;
1722 
1723  case dicom::PALETTE_COLOR:
1724  if( dicm.contain( 0x0028, 0x1201 ) && dicm.contain( 0x0028, 0x1202 ) && dicm.contain( 0x0028, 0x1203 ) )
1725  {
1726  const dicom::dicom_element &red = dicm( 0x0028, 0x1201 );
1727  const dicom::dicom_element &green = dicm( 0x0028, 0x1202 );
1728  const dicom::dicom_element &blue = dicm( 0x0028, 0x1203 );
1729 
1730  const short *r = reinterpret_cast< const short * >( red.data );
1731  const short *g = reinterpret_cast< const short * >( green.data );
1732  const short *b = reinterpret_cast< const short * >( blue.data );
1733 
1734  bool is_big_endian = false;
1735 
1736  int max_value = ( 2 << info.bits_stored ) - 1;
1737  for( size_t i = 0 ; i < red.num_bytes / 2 ; i++ )
1738  {
1739  if( r[ i ] > max_value || g[ i ] > max_value || b[ i ] > max_value )
1740  {
1741  is_big_endian = true;
1742  break;
1743  }
1744  }
1745 
1746  if( is_big_endian )
1747  {
1748  // パレットのデータは,必ずビッグエンディアンでインプリメントされている
1749  for( size_type j = 0 ; j < image.height( ) ; j++ )
1750  {
1751  unsigned char *data = element.data + j * image.width( );
1752  for( size_type i = 0 ; i < image.width( ) ; i++ )
1753  {
1754  unsigned char R = static_cast< unsigned char >( from_current_endian( byte_array< short >( r[ data[ i ] ] ), false ).get_value( ) );
1755  unsigned char G = static_cast< unsigned char >( from_current_endian( byte_array< short >( g[ data[ i ] ] ), false ).get_value( ) );
1756  unsigned char B = static_cast< unsigned char >( from_current_endian( byte_array< short >( b[ data[ i ] ] ), false ).get_value( ) );
1757  image( i, j ) = pixel_converter::convert_to( R, G, B );
1758  }
1759  }
1760  }
1761  else
1762  {
1763  // パレットのデータは,必ずビッグエンディアンでインプリメントされている
1764  for( size_type j = 0 ; j < image.height( ) ; j++ )
1765  {
1766  unsigned char *data = element.data + j * image.width( );
1767  for( size_type i = 0 ; i < image.width( ) ; i++ )
1768  {
1769  unsigned char R = static_cast< unsigned char >( r[ data[ i ] ] );
1770  unsigned char G = static_cast< unsigned char >( g[ data[ i ] ] );
1771  unsigned char B = static_cast< unsigned char >( b[ data[ i ] ] );
1772  image( i, j ) = pixel_converter::convert_to( R, G, B );
1773  }
1774  }
1775  }
1776  }
1777  break;
1778 
1779  case dicom::MONOCHROME1:
1780  case dicom::MONOCHROME2:
1781  default:
1782  if( info.bits_allocated == 8 )
1783  {
1784  for( size_type j = 0 ; j < image.height( ) ; j++ )
1785  {
1786  unsigned char *data = element.data + j * image.width( );
1787  for( size_type i = 0 ; i < image.width( ) ; i++ )
1788  {
1789  image( i, j ) = data[ i ];
1790  }
1791  }
1792  }
1793  else if( info.bits_allocated == 16 )
1794  {
1795  // MONOCHROME1 では背景が白がデフォルト
1796  // MONOCHROME2 では背景が黒がデフォルト
1797  if( info.photometric_type == dicom::MONOCHROME1 )
1798  {
1799  window_width = -window_width;
1800  }
1801 
1802  double offset = info.rescale_intercept;
1803  if( dicm.contain( 0x0028, 0x1050 ) && dicm.contain( 0x0028, 0x1051 ) && dicm( 0x0028, 0x1050 ).num_bytes > 0 && dicm( 0x0028, 0x1051 ).num_bytes > 0 )
1804  {
1805  const short *bytes = reinterpret_cast< const short * >( element.data );
1806  for( size_type j = 0 ; j < image.height( ) ; j++ )
1807  {
1808  const short *data = bytes + j * image.width( );
1809  for( size_type i = 0 ; i < image.width( ) ; i++ )
1810  {
1811  double pix = ( ( data[ i ] + offset - window_level ) / window_width + 0.5 ) * 255.0;
1812  pix = pix > 255.0 ? 255.0 : pix;
1813  pix = pix < 0.0 ? 0.0 : pix;
1814  image( i, j ) = static_cast< unsigned char >( pix );
1815  }
1816  }
1817  }
1818  else
1819  {
1820  // まず,描画に用いる範囲を決定する
1821  const unsigned short *bytes = reinterpret_cast< const unsigned short * >( element.data );
1822  double min = bytes[ 0 ], max = bytes[ 0 ];
1823  for( size_t l = 1 ; l < element.num_bytes / 2 ; l++ )
1824  {
1825  if( bytes[ l ] < min )
1826  {
1827  min = bytes[ l ];
1828  }
1829  else if( max < bytes[ l ] )
1830  {
1831  max = bytes[ l ];
1832  }
1833  }
1834 
1835  double ww = max - min + 1;
1836  double wl = ( max + min ) / 2.0;
1837 
1838  // MONOCHROME1 では背景が白がデフォルト
1839  // MONOCHROME2 では背景が黒がデフォルト
1840  if( info.photometric_type == dicom::MONOCHROME1 )
1841  {
1842  ww = -ww;
1843  }
1844 
1845  for( size_type j = 0 ; j < image.height( ) ; j++ )
1846  {
1847  const unsigned short *data = bytes + j * image.width( );
1848  for( size_type i = 0 ; i < image.width( ) ; i++ )
1849  {
1850  double pix = ( ( data[ i ] - wl ) / ww + 0.5 ) * 255.0;
1851  pix = pix > 255.0 ? 255.0 : pix;
1852  pix = pix < 0.0 ? 0.0 : pix;
1853  image( i, j ) = static_cast< unsigned char >( pix );
1854  }
1855  }
1856  }
1857  }
1858  else
1859  {
1860  return( false );
1861  }
1862  break;
1863  }
1864  }
1865  else
1866  {
1867  return( false );
1868  }
1869 
1870  return( true );
1871 }
1872 
1873 
1875 // DICOM画像入出力グループの終わり
1876 
1878 // 画像入出力グループの終わり
1879 
1880 
1881 // mist名前空間の終わり
1882 _MIST_END
1883 
1884 
1885 #endif // __INCLUDE_MIST_DICOM__
1886 

Generated on Wed Nov 12 2014 19:44:14 for MIST by doxygen 1.8.1.2