il_dicom.c
Upload User: wmy0603
Upload Date: 2022-05-02
Package Size: 1808k
Code Size: 15k
Development Platform:

Visual C++

  1. //-----------------------------------------------------------------------------
  2. //
  3. // ImageLib Sources
  4. // Copyright (C) 2000-2009 by Denton Woods
  5. // Last modified: 02/14/2009
  6. //
  7. // Filename: src-IL/src/il_dicom.c
  8. //
  9. // Description: Reads from a Digital Imaging and Communications in Medicine
  10. // (DICOM) file.  Specifications can be found at 
  11. //                http://en.wikipedia.org/wiki/Dicom.
  12. //
  13. //-----------------------------------------------------------------------------
  14. #include "il_internal.h"
  15. #ifndef IL_NO_DICOM
  16. typedef struct DICOMHEAD
  17. {
  18. ILubyte Signature[4];
  19. ILuint Version;
  20. ILuint Width;
  21. ILuint Height;
  22. ILuint Depth;
  23. ILuint Samples;
  24. ILuint BitsAllocated;
  25. ILuint BitsStored;
  26. ILuint DataLen;
  27. ILboolean BigEndian;
  28. ILenum Encoding;
  29. // For DevIL use only
  30. ILenum Format;
  31. ILenum Type;
  32. } DICOMHEAD;
  33. ILboolean iIsValidDicom(void);
  34. ILboolean iCheckDicom(DICOMHEAD *Header);
  35. ILboolean iLoadDicomInternal(void);
  36. ILboolean iGetDicomHead(DICOMHEAD *Header);
  37. ILboolean SkipElement(DICOMHEAD *Header, ILushort GroupNum, ILushort ElementNum);
  38. ILboolean GetNumericValue(DICOMHEAD *Header, ILushort GroupNum, ILuint *Number);
  39. ILboolean GetUID(ILubyte *UID);
  40. ILuint GetGroupNum(DICOMHEAD *Header);
  41. ILuint GetShort(DICOMHEAD *Header, ILushort GroupNum);
  42. ILuint GetInt(DICOMHEAD *Header, ILushort GroupNum);
  43. ILfloat GetFloat(DICOMHEAD *Header, ILushort GroupNum);
  44. //! Checks if the file specified in FileName is a valid DICOM file.
  45. ILboolean ilIsValid_DICOM(ILconst_string FileName)
  46. {
  47. ILHANDLE DicomFile;
  48. ILboolean bDicom = IL_FALSE;
  49. if (!iCheckExtension(FileName, IL_TEXT("dicom")) && !iCheckExtension(FileName, IL_TEXT("dcm"))) {
  50. ilSetError(IL_INVALID_EXTENSION);
  51. return bDicom;
  52. }
  53. DicomFile = iopenr(FileName);
  54. if (DicomFile == NULL) {
  55. ilSetError(IL_COULD_NOT_OPEN_FILE);
  56. return bDicom;
  57. }
  58. bDicom = ilIsValidF_DICOM(DicomFile);
  59. icloser(DicomFile);
  60. return bDicom;
  61. }
  62. //! Checks if the ILHANDLE contains a valid DICOM file at the current position.
  63. ILboolean ilIsValidF_DICOM(ILHANDLE File)
  64. {
  65. ILuint FirstPos;
  66. ILboolean bRet;
  67. iSetInputFile(File);
  68. FirstPos = itell();
  69. bRet = iIsValidDicom();
  70. iseek(FirstPos, IL_SEEK_SET);
  71. return bRet;
  72. }
  73. //! Checks if Lump is a valid DICOM lump.
  74. ILboolean ilIsValidL_DICOM(const void *Lump, ILuint Size)
  75. {
  76. iSetInputLump(Lump, Size);
  77. return iIsValidDicom();
  78. }
  79. // Internal function to get the header and check it.
  80. ILboolean iIsValidDicom(void)
  81. {
  82. DICOMHEAD Header;
  83. ILuint Pos = itell();
  84. // Clear the header to all 0s to make checks later easier.
  85. memset(&Header, 0, sizeof(DICOMHEAD));
  86. if (!iGetDicomHead(&Header))
  87. return IL_FALSE;
  88. // The length of the header varies, so we just go back to the original position.
  89. iseek(Pos, IL_SEEK_CUR);
  90. return iCheckDicom(&Header);
  91. }
  92. // Internal function used to get the DICOM header from the current file.
  93. ILboolean iGetDicomHead(DICOMHEAD *Header)
  94. {
  95. ILushort GroupNum, ElementNum;
  96. ILboolean ReachedData = IL_FALSE;
  97. ILubyte Var2, UID[65];
  98. // Signature should be "DICM" at position 128.
  99. iseek(128, IL_SEEK_SET);
  100. if (iread(Header->Signature, 1, 4) != 4)
  101. return IL_FALSE;
  102. //@TODO: What about the case when we are reading an image with Big Endian data?
  103. do {
  104. GroupNum = GetGroupNum(Header);
  105. ElementNum = GetShort(Header, GroupNum);;
  106. switch (GroupNum)
  107. {
  108. case 0x02:
  109. switch (ElementNum)
  110. {
  111. /*case 0x01:  // Version number
  112. if (!GetNumericValue(&Header->Version))
  113. return IL_FALSE;
  114. if (Header->Version != 0x0100)
  115. return IL_FALSE;
  116. break;*/
  117. case 0x10:
  118. //@TODO: Look at pg. 60 of 07_05pu.pdf (PS 3.5) for more UIDs.
  119. if (!GetUID(UID))
  120. return IL_FALSE;
  121. if (!strncmp(UID, "1.2.840.10008.1.2.2", 64))  // Explicit big endian
  122. Header->BigEndian = IL_TRUE;
  123. else if (!strncmp(UID, "1.2.840.10008.1.2.1", 64))  // Explicit little endian
  124. Header->BigEndian = IL_FALSE;
  125. else if (!strncmp(UID, "1.2.840.10008.1.2", 64))  // Implicit little endian
  126. Header->BigEndian = IL_FALSE;
  127. else 
  128. return IL_FALSE;  // Unrecognized UID.
  129. break;
  130. default:
  131. if (!SkipElement(Header, GroupNum, ElementNum))  // We do not understand this entry, so we just skip it.
  132. return IL_FALSE;
  133. }
  134. break;
  135. case 0x28:
  136. switch (ElementNum)
  137. {
  138. case 0x02:  // Samples per pixel
  139. if (!GetNumericValue(Header, GroupNum, &Header->Samples))
  140. return IL_FALSE;
  141. break;
  142. case 0x08:  // Number of frames, or depth
  143. if (!GetNumericValue(Header, GroupNum, &Header->Depth))
  144. return IL_FALSE;
  145. break;
  146. case 0x10:  // The number of rows
  147. if (!GetNumericValue(Header, GroupNum, &Header->Height))
  148. return IL_FALSE;
  149. break;
  150. case 0x11:  // The number of columns
  151. if (!GetNumericValue(Header, GroupNum, &Header->Width))
  152. return IL_FALSE;
  153. break;
  154. case 0x100:  // Bits allocated per sample
  155. if (!GetNumericValue(Header, GroupNum, &Header->BitsAllocated))
  156. return IL_FALSE;
  157. break;
  158. case 0x101:  // Bits stored per sample - Do we really need this information?
  159. if (!GetNumericValue(Header, GroupNum, &Header->BitsStored))
  160. return IL_FALSE;
  161. break;
  162. default:
  163. if (!SkipElement(Header, GroupNum, ElementNum))  // We do not understand this entry, so we just skip it.
  164. return IL_FALSE;
  165. }
  166. break;
  167. case 0x7FE0:
  168. switch (ElementNum)
  169. {
  170. case 0x10:  // This element is the actual pixel data.  We are done with the header here.
  171. if (igetc() != 'O')  // @TODO: Can we assume that this is always 'O'?
  172. return IL_FALSE;
  173. Var2 = igetc();
  174. if (Var2 != 'B' && Var2 != 'W' && Var2 != 'F')  // 'OB', 'OW' and 'OF' accepted for this element.
  175. return IL_FALSE;
  176. GetLittleUShort();  // Skip the 2 reserved bytes.
  177. Header->DataLen = GetInt(Header, GroupNum);//GetLittleUInt();
  178. ReachedData = IL_TRUE;
  179. break;
  180. default:
  181. if (!SkipElement(Header, GroupNum, ElementNum))  // We do not understand this entry, so we just skip it.
  182. return IL_FALSE;
  183. }
  184. break;
  185. default:
  186. if (!SkipElement(Header, GroupNum, ElementNum))  // We do not understand this entry, so we just skip it.
  187. return IL_FALSE;
  188. }
  189. } while (!ieof() && !ReachedData);
  190. if (ieof())
  191. return IL_FALSE;
  192. // Some DICOM images do not have the depth (number of frames) field.
  193. if (Header->Depth == 0)
  194. Header->Depth = 1;
  195. switch (Header->BitsAllocated)
  196. {
  197. case 8:
  198. Header->Type = IL_UNSIGNED_BYTE;
  199. break;
  200. case 16:
  201. Header->Type = IL_UNSIGNED_SHORT;
  202. break;
  203. case 32:
  204. Header->Type = IL_FLOAT;  //@TODO: Is this ever an integer?
  205. break;
  206. default:  //@TODO: Any other types we can deal with?
  207. return IL_FALSE;
  208. }
  209. // Cannot handle more than 4 channels in an image.
  210. if (Header->Samples > 4)
  211. return IL_FALSE;
  212. Header->Format = ilGetFormatBpp(Header->Samples);
  213. return IL_TRUE;
  214. }
  215. ILboolean SkipElement(DICOMHEAD *Header, ILushort GroupNum, ILushort ElementNum)
  216. {
  217. ILubyte VR1, VR2;
  218. ILuint ValLen;
  219. // 2 byte character string telling what type this element is ('OB', 'UI', etc.)
  220. VR1 = igetc();
  221. VR2 = igetc();
  222. if ((VR1 == 'O' && VR2 == 'B') || (VR1 == 'O' && VR2 == 'W') || (VR1 == 'O' && VR2 == 'F') ||
  223. (VR1 == 'S' && VR2 == 'Q') || (VR1 == 'U' && VR2 == 'T') || (VR1 == 'U' && VR2 == 'N')) {
  224. // These all have a different format than the other formats, since they can be up to 32 bits long.
  225. GetLittleUShort();  // Values reserved, we do not care about them.
  226. ValLen = GetInt(Header, GroupNum);//GetLittleUInt();  // Length of the rest of the element
  227. if (ValLen % 2)  // This length must be even, according to the specs.
  228. return IL_FALSE;
  229. if (ElementNum != 0x00)  // Element numbers of 0 tell the size of the full group, so we do not skip this.
  230.  //  @TODO: We could use this to skip groups that we do not care about.
  231. if (iseek(ValLen, IL_SEEK_CUR))
  232. return IL_FALSE;
  233. }
  234. else {
  235. // These have a length of 16 bits.
  236. ValLen = GetShort(Header, GroupNum);//GetLittleUShort();
  237. //if (ValLen % 2)  // This length must be even, according to the specs.
  238. // ValLen++;  // Add the extra byte to seek.
  239. //if (ElementNum != 0x00)  // Element numbers of 0 tell the size of the full group, so we do not skip this.
  240.  //  @TODO: We could use this to skip groups that we do not care about.
  241. if (iseek(ValLen, IL_SEEK_CUR))
  242. return IL_FALSE;
  243. }
  244. return IL_TRUE;
  245. }
  246. ILuint GetGroupNum(DICOMHEAD *Header)
  247. {
  248. ILushort GroupNum;
  249. iread(&GroupNum, 1, 2);
  250. // The 0x02 group is always little endian.
  251. if (GroupNum == 0x02) {
  252. UShort(&GroupNum);
  253. return GroupNum;
  254. }
  255. // Now we have to swizzle it if it is not 0x02.
  256. if (Header->BigEndian)
  257. BigUShort(&GroupNum);
  258. else
  259. UShort(&GroupNum);
  260. return GroupNum;
  261. }
  262. ILuint GetShort(DICOMHEAD *Header, ILushort GroupNum)
  263. {
  264. ILushort Num;
  265. iread(&Num, 1, 2);
  266. // The 0x02 group is always little endian.
  267. if (GroupNum == 0x02) {
  268. UShort(&Num);
  269. return Num;
  270. }
  271. // Now we have to swizzle it if it is not 0x02.
  272. if (Header->BigEndian)
  273. BigUShort(&Num);
  274. else
  275. UShort(&Num);
  276. return Num;
  277. }
  278. ILuint GetInt(DICOMHEAD *Header, ILushort GroupNum)
  279. {
  280. ILuint Num;
  281. iread(&Num, 1, 4);
  282. // The 0x02 group is always little endian.
  283. if (GroupNum == 0x02) {
  284. UInt(&Num);
  285. return Num;
  286. }
  287. // Now we have to swizzle it if it is not 0x02.
  288. if (Header->BigEndian)
  289. BigUInt(&Num);
  290. else
  291. UInt(&Num);
  292. return Num;
  293. }
  294. ILfloat GetFloat(DICOMHEAD *Header, ILushort GroupNum)
  295. {
  296. ILfloat Num;
  297. iread(&Num, 1, 4);
  298. // The 0x02 group is always little endian.
  299. if (GroupNum == 0x02) {
  300. Float(&Num);
  301. return Num;
  302. }
  303. // Now we have to swizzle it if it is not 0x02.
  304. if (Header->BigEndian)
  305. BigFloat(&Num);
  306. else
  307. Float(&Num);
  308. return Num;
  309. }
  310. ILboolean GetNumericValue(DICOMHEAD *Header, ILushort GroupNum, ILuint *Number)
  311. {
  312. ILubyte VR1, VR2;
  313. ILushort ValLen;
  314. // 2 byte character string telling what type this element is ('OB', 'UI', etc.)
  315. VR1 = igetc();
  316. VR2 = igetc();
  317. if (VR1 == 'U' && VR2 == 'S') {  // Unsigned short
  318. ValLen = GetShort(Header, GroupNum);//GetLittleUShort();
  319. if (ValLen != 2)  // Must always be 2 for short ('US')
  320. return IL_FALSE;
  321. *((ILushort*)Number) = GetShort(Header, GroupNum);//GetLittleUShort();
  322. return IL_TRUE;
  323. }
  324. if (VR1 == 'U' && VR2 == 'L') {  // Unsigned long
  325. ValLen = GetInt(Header, GroupNum);//GetLittleUInt();
  326. if (ValLen != 4)  // Must always be 4 for long ('UL')
  327. return IL_FALSE;
  328. *Number = GetInt(Header, GroupNum);
  329. return IL_TRUE;
  330. }
  331. if (VR1 == 'S' && VR2 == 'S') {  // Signed short
  332. ValLen = GetShort(Header, GroupNum);
  333. if (ValLen != 2)  // Must always be 2 for short ('US')
  334. return IL_FALSE;
  335. *((ILshort*)Number) = GetShort(Header, GroupNum);
  336. return IL_TRUE;
  337. }
  338. if (VR1 == 'S' && VR2 == 'L') {  // Signed long
  339. ValLen = GetInt(Header, GroupNum);
  340. if (ValLen != 4)  // Must always be 4 for long ('UL')
  341. return IL_FALSE;
  342. *((ILint*)Number) = GetInt(Header, GroupNum);
  343. return IL_TRUE;
  344. }
  345. return IL_FALSE;
  346. }
  347. ILboolean GetUID(ILubyte *UID)
  348. {
  349. ILubyte VR1, VR2;
  350. ILushort ValLen;
  351. // 2 byte character string telling what type this element is ('OB', 'UI', etc.)
  352. VR1 = igetc();
  353. VR2 = igetc();
  354. if (VR1 != 'U' || VR2 != 'I')  // 'UI' == UID
  355. return IL_FALSE;
  356. ValLen = GetLittleUShort();
  357. if (ValLen > 64)
  358. return IL_FALSE;
  359. if (iread(UID, ValLen, 1) != 1)
  360. return IL_FALSE;
  361. UID[ValLen] = 0;  // Just to make sure that our string is terminated.
  362. return IL_TRUE;
  363. }
  364. // Internal function used to check if the HEADER is a valid DICOM header.
  365. ILboolean iCheckDicom(DICOMHEAD *Header)
  366. {
  367. // Always has the signature "DICM" at position 0x80.
  368. if (strncmp(Header->Signature, "DICM", 4))
  369. return IL_FALSE;
  370. // Does not make sense to have any dimension = 0.
  371. if (Header->Width == 0 || Header->Height == 0 || Header->Depth == 0)
  372. return IL_FALSE;
  373. // We can only deal with images that have byte-aligned data.
  374. //@TODO: Take care of any others?
  375. if (Header->BitsAllocated % 8)
  376. return IL_FALSE;
  377. // Check for an invalid format being set (or not set at all).
  378. if (ilGetBppFormat(Header->Format) == 0)
  379. return IL_FALSE;
  380. // Check for an invalid type being set (or not set at all).
  381. if (ilGetBpcType(Header->Type) == 0)
  382. return IL_FALSE;
  383. return IL_TRUE;
  384. }
  385. //! Reads a DICOM file
  386. ILboolean ilLoad_DICOM(ILconst_string FileName)
  387. {
  388. ILHANDLE DicomFile;
  389. ILboolean bDicom = IL_FALSE;
  390. DicomFile = iopenr(FileName);
  391. if (DicomFile == NULL) {
  392. ilSetError(IL_COULD_NOT_OPEN_FILE);
  393. return bDicom;
  394. }
  395. bDicom = ilLoadF_DICOM(DicomFile);
  396. icloser(DicomFile);
  397. return bDicom;
  398. }
  399. //! Reads an already-opened DICOM file
  400. ILboolean ilLoadF_DICOM(ILHANDLE File)
  401. {
  402. ILuint FirstPos;
  403. ILboolean bRet;
  404. iSetInputFile(File);
  405. FirstPos = itell();
  406. bRet = iLoadDicomInternal();
  407. iseek(FirstPos, IL_SEEK_SET);
  408. return bRet;
  409. }
  410. //! Reads from a memory "lump" that contains a DICOM
  411. ILboolean ilLoadL_DICOM(const void *Lump, ILuint Size)
  412. {
  413. iSetInputLump(Lump, Size);
  414. return iLoadDicomInternal();
  415. }
  416. // Internal function used to load the DICOM.
  417. ILboolean iLoadDicomInternal(void)
  418. {
  419. DICOMHEAD Header;
  420. ILuint i;
  421. ILushort TempS, *ShortPtr;
  422. ILfloat TempF, *FloatPtr;
  423. ILboolean Swizzle = IL_FALSE;
  424. if (iCurImage == NULL) {
  425. ilSetError(IL_ILLEGAL_OPERATION);
  426. return IL_FALSE;
  427. }
  428. // Clear the header to all 0s to make checks later easier.
  429. memset(&Header, 0, sizeof(DICOMHEAD));
  430. if (!iGetDicomHead(&Header)) {
  431. ilSetError(IL_INVALID_FILE_HEADER);
  432. return IL_FALSE;
  433. }
  434. if (!iCheckDicom(&Header))
  435. return IL_FALSE;
  436. if (!ilTexImage(Header.Width, Header.Height, Header.Depth, ilGetBppFormat(Header.Format), Header.Format, Header.Type, NULL))
  437. return IL_FALSE;
  438. //@TODO: Find out if the origin is always in the upper left.
  439. iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
  440. // Header.DataLen may be larger than SizeOfData, since it has to be padded with a NULL if it is not an even length,
  441. //   so we just test to make sure it is at least large enough.
  442. //@TODO: Do this check before ilTexImage call.
  443. if (Header.DataLen < iCurImage->SizeOfData) {
  444. ilSetError(IL_INVALID_FILE_HEADER);
  445. return IL_FALSE;
  446. }
  447. // We may have to swap the order of the data.
  448. #ifdef __BIG_ENDIAN__
  449. if (!Header.BigEndian) {
  450. if (Header.Format == IL_RGB)
  451. Header.Format = IL_BGR;
  452. else if (Header.Format == IL_RGBA)
  453. Swizzle = IL_TRUE;
  454. }
  455. #else  // Little endian
  456. if (Header.BigEndian) {
  457. if (Header.Format == IL_RGB)
  458. Header.Format = IL_BGR;
  459. if (Header.Format == IL_RGBA)
  460. Swizzle = IL_TRUE;
  461. }
  462. #endif
  463. switch (Header.Type)
  464. {
  465. case IL_UNSIGNED_BYTE:
  466. if (iread(iCurImage->Data, iCurImage->SizeOfData, 1) != 1)
  467. return IL_FALSE;
  468. // Swizzle the data from ABGR to RGBA.
  469. if (Swizzle) {
  470. for (i = 0; i < iCurImage->SizeOfData; i += 4) {
  471. iSwapUInt((ILuint*)(iCurImage->Data + i));
  472. }
  473. }
  474. break;
  475. case IL_UNSIGNED_SHORT:
  476. for (i = 0; i < iCurImage->SizeOfData; i += 2) {
  477. *((ILushort*)(iCurImage->Data + i)) = GetShort(&Header, 0);//GetLittleUShort();
  478. }
  479. // Swizzle the data from ABGR to RGBA.
  480. if (Swizzle) {
  481. ShortPtr = (ILushort*)iCurImage->Data;
  482. for (i = 0; i < iCurImage->SizeOfData / 2; i += 4) {
  483. TempS = ShortPtr[i];
  484. ShortPtr[i] = ShortPtr[i+3];
  485. ShortPtr[i+3] = TempS;
  486. }
  487. }
  488. break;
  489. case IL_FLOAT:
  490. for (i = 0; i < iCurImage->SizeOfData; i += 4) {
  491. *((ILfloat*)(iCurImage->Data + i)) = GetFloat(&Header, 0);//GetLittleFloat();
  492. }
  493. // Swizzle the data from ABGR to RGBA.
  494. if (Swizzle) {
  495. FloatPtr = (ILfloat*)iCurImage->Data;
  496. for (i = 0; i < iCurImage->SizeOfData / 4; i += 4) {
  497. TempF = FloatPtr[i];
  498. FloatPtr[i] = FloatPtr[i+3];
  499. FloatPtr[i+3] = TempF;
  500. }
  501. }
  502. break;
  503. }
  504. return ilFixImage();
  505. }
  506. #endif//IL_NO_DICOM