dcm.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. /*
  2. Example:
  3. dcm d;
  4. d.Load(filename.c_str());
  5. WORD *pImg = (WORD*)(d.ppData[i]);
  6. memcpy(pInArray[ 1 ], (WORD*)(d.ppData[i]), d.nHeight * d.nWidth * sizeof(WORD));
  7. */
  8. #pragma once
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string>
  12. #include <sstream> //使用stringstream需要引入这个头文件
  13. #include "LogLocalHelper.h"
  14. #include "Log4CPP.h"
  15. using namespace std;
  16. //extern Log4CPP::Logger* mLog::gLogger;
  17. class dcm
  18. {
  19. public:
  20. int nWidth, nHeight, nBits, nFrames, nWindowCenter, nWindowWidth;
  21. void ** ppData;
  22. string PatientName, SOPInstanceUID;
  23. int filesize;
  24. bool bLoaded;
  25. dcm() : nWidth(0), nHeight(0), nBits(0), ppData(0), bLoaded(false)
  26. {
  27. };
  28. ~dcm()
  29. {
  30. Destroy();
  31. }
  32. void Destroy()
  33. {
  34. if (ppData != NULL)
  35. {
  36. for (int i = 0; i < nFrames; i++)
  37. {
  38. if (ppData[i] != NULL)
  39. {
  40. free(ppData[i]);
  41. ppData[i] = NULL;
  42. }
  43. }
  44. free(ppData);
  45. ppData = NULL;
  46. }
  47. nWidth = nHeight = nBits = 0;
  48. bLoaded = false;
  49. };
  50. unsigned short & operator()(int h, int w, int f = 0) { return ((unsigned short*)(ppData[f]))[h * nWidth + w]; }
  51. //unsigned short & operator()(int index, int f = 0) { return ((unsigned short*)(ppData[f]))[index]; }
  52. void Load_raw_file(const char* filename)
  53. {
  54. char tmp[64] = { 0 }, str[32] = { 0 };
  55. char *p1, *p2;
  56. strncpy(tmp, filename, sizeof(tmp) - 1);
  57. tmp[strlen(filename) - 4] = '\0';
  58. p2 = strrchr(tmp, '_');
  59. int loc2 = p2 - tmp;
  60. strncpy(str, p2 + 1, sizeof(str) - 1);
  61. nHeight = atoi(str);
  62. tmp[loc2] = '\0';
  63. p1 = strrchr(tmp, '_');
  64. int loc1 = p1 - tmp;
  65. strncpy(str, p1 + 1, sizeof(str) - 1);
  66. nWidth = atoi(str);
  67. nBits = 16;
  68. nFrames = 1;
  69. ppData = (void**)malloc(sizeof(void*) * nFrames);
  70. ppData[0] = (unsigned short*)malloc(sizeof(unsigned short) * nHeight * nWidth);
  71. FILE* fid = fopen(filename, "rb");
  72. if (fid != NULL) {
  73. size_t nBytes = fread(ppData[0], sizeof(unsigned short), nHeight * nWidth, fid);
  74. fclose(fid);
  75. bLoaded = true;
  76. }
  77. else {
  78. // 处理文件打开失败的情况
  79. bLoaded = false;
  80. // 可以在这里添加错误处理代码,如释放已分配的内存
  81. if (ppData != NULL) {
  82. free(ppData[0]);
  83. free(ppData);
  84. ppData = NULL;
  85. }
  86. }
  87. bLoaded = true;
  88. }
  89. bool Load(const char* filename)
  90. {
  91. bool isLitteEndian = false;//是否小字节序(小端在前 、大端在前)
  92. bool isExplicitVR = false;//有无VR
  93. // key info for image data
  94. int height = 0, width = 0, nHighBit = 0, nBitsAllocated = 0;
  95. bool bSigned = false;
  96. int windowCenter = 0, windowWidth = 0;
  97. double rescaleIntercept, rescaleSlope;
  98. bool bInverse = false;
  99. nFrames = 1;
  100. int nRet = 0;
  101. isLitteEndian = true;//是否小字节序(小端在前 、大端在前)
  102. isExplicitVR = true;//有无VR
  103. // read tag,VR,VF...
  104. int tag = 0;
  105. std::string VR, VF;
  106. int VFlen = 0;
  107. // info folder
  108. bool endDir = false;
  109. int level = 0;
  110. FILE *f = 0;
  111. f = fopen(filename, "rb");
  112. if (f == NULL)
  113. {
  114. FERROR("open %s fail!", filename);
  115. return false;
  116. }
  117. fseek(f, 0L, SEEK_END); /* 定位到文件末尾 */
  118. filesize = ftell(f); /* 得到文件大小 */
  119. FINFO("file size:{$}", filesize);
  120. fseek(f, 0L, SEEK_SET); /* 定位到文件开头 */
  121. fseek(f, 128, SEEK_SET);
  122. if (!(getString(f, 4) == "DICM"))
  123. {
  124. fclose(f);
  125. FERROR("getString fail!");
  126. return false;
  127. }
  128. while (true)
  129. {
  130. tag = getTag(f, isLitteEndian);
  131. if (tag == 0)
  132. {
  133. int len = ftell(f);
  134. FINFO("offset length:{$}",len);
  135. fclose(f);
  136. FERROR("getTag fail!");
  137. return false;
  138. }
  139. // getVR
  140. if (isExplicitVR == false)
  141. {
  142. VR = getInExplicitVR(tag);
  143. VFlen = getInt32(f, isLitteEndian);
  144. }
  145. else
  146. {
  147. if ((tag & 0x000f0000) == 0x00020000)//文件头 特殊情况
  148. {
  149. VR = getVR(f);
  150. if (VR == "OB" || VR == "OW" || VR == "SQ" || VR == "OF" || VR == "UT" || VR == "UN")
  151. {
  152. nRet = fseek(f, 2, SEEK_CUR);
  153. if (nRet)
  154. {
  155. FERROR("fseek error! offset:{$}", ftell(f));
  156. }
  157. VFlen = getInt32(f, isLitteEndian);
  158. }
  159. else
  160. {
  161. VFlen = getShort16(f, isLitteEndian);
  162. }
  163. //FINFO("111 VR:{$},VFlen:{$}", VR, VFlen);
  164. }
  165. else if (tag == 0xffffe000 || tag == 0xffffe00d || tag == 0xffffe0dd)//文件夹标签
  166. {
  167. VR = "**";
  168. VFlen = getInt32(f, isLitteEndian);
  169. //FINFO("222 VR:{$},VFlen:{$}", VR, VFlen);
  170. }
  171. else
  172. {
  173. VR = getVR(f);
  174. if (VR == "OB" || VR == "OW" || VR == "SQ" || VR == "OF" || VR == "UT" || VR == "UN")
  175. {
  176. nRet = fseek(f, 2, SEEK_CUR);
  177. if (nRet)
  178. {
  179. FERROR("fseek error! offset:{$}", ftell(f));
  180. }
  181. VFlen = getInt32(f, isLitteEndian);
  182. }
  183. else
  184. {
  185. VFlen = getShort16(f, isLitteEndian);
  186. }
  187. //FINFO("333 VR:{$},VFlen:{$}", VR, VFlen);
  188. }
  189. }
  190. // getVF
  191. //判断是否应该读取VF 以何种方式读取VF
  192. //--------------这些都是在读取VF一步被阻断的情况
  193. if ((tag == 0xffffe000))//遇到文件夹开始标签了
  194. {
  195. level = 1;//VF不赋值
  196. }
  197. else if ((tag == 0xffffe0dd))//文件夹结束标签
  198. {
  199. level = 0;
  200. }
  201. else
  202. {
  203. //FINFO("level:{$}", level);
  204. if (level == 1)
  205. {
  206. if ((tag == 0x7fe00010))//图像数据开始了
  207. {
  208. nRet = fseek(f, VFlen, SEEK_CUR);
  209. if (nRet)
  210. {
  211. FERROR("fseek error! offset:{$}", ftell(f));
  212. }
  213. }
  214. else
  215. {
  216. VF = getString(f, VFlen);
  217. }
  218. }
  219. else if (level == 0)
  220. {
  221. if ((tag == 0x7fe00010))//图像数据开始了
  222. {
  223. FINFO("read image data begin nFrames:{$}", nFrames);
  224. ppData = (void**)malloc(sizeof(void*) * nFrames);
  225. for (int i = 0; i < nFrames; i++)
  226. {
  227. ppData[i] = (unsigned short*)malloc(sizeof(unsigned short) * height * width);
  228. fread(ppData[i], sizeof(unsigned short) * height * width, 1, f);
  229. }
  230. bLoaded = true;
  231. break;
  232. }
  233. else
  234. {
  235. VF = getString(f, VFlen);
  236. //--------针对特殊的tag的值的处理
  237. //特别针对文件头信息处理
  238. if (tag == 0x00020000)
  239. {
  240. //FINFO("tag == 0x00020000");
  241. }
  242. else if (tag == 0x00020010)//传输语法 关系到后面的数据读取
  243. {
  244. //FINFO("tag == 0x00020010 VF == {$}", VF);
  245. if (VF == "1.2.840.10008.1.2.1\0")//显示little
  246. {
  247. isLitteEndian = true;
  248. isExplicitVR = true;
  249. }
  250. else if (VF == "1.2.840.10008.1.2.2\0")//显示big
  251. {
  252. isLitteEndian = false;
  253. isExplicitVR = true;
  254. }
  255. else if (VF == "1.2.840.10008.1.2\0")//隐式little
  256. {
  257. isLitteEndian = true;
  258. isExplicitVR = false;
  259. }
  260. }
  261. else if (tag == 0x00080018)
  262. {
  263. SOPInstanceUID = VF;
  264. FINFO("tag == 0x00080018 VF == {$}, SOPInstanceUID:{$}", VF, SOPInstanceUID);
  265. }
  266. else if (tag == 0x00100010)
  267. {
  268. PatientName = VF;
  269. string str = PatientName;
  270. size_t pLeft = str.find('\\');
  271. size_t pRight = str.find('.');
  272. if (pRight > -1)
  273. {
  274. std::string ext = str.substr(pRight);
  275. if (ext == ".raw")
  276. PatientName = str.substr(pLeft + 1, pRight - pLeft - 1);
  277. }
  278. FINFO("tag == 0x00100010 VF == {$}, PatientName:{$}", VF, PatientName);
  279. }
  280. else if (tag == 0x00280004)
  281. {
  282. if (VF == "MONOCHROME2")
  283. bInverse = true;
  284. FINFO("tag == 0x00280004 VF == {$}", VF);
  285. }
  286. else if (tag == 0x00280008)
  287. {
  288. nFrames = str2int(VF);
  289. FINFO("tag == 0x00280008 VF == {$}, nFrames:{$}", VF, nFrames);
  290. }
  291. else if (tag == 0x00280010)
  292. {
  293. nRet = fseek(f, -2, SEEK_CUR);
  294. if (nRet)
  295. {
  296. FERROR("fseek error! offset:{$}", ftell(f));
  297. }
  298. height = getShort16(f, isLitteEndian);
  299. FINFO("tag == 0x00280010 height == {$}", height);
  300. }
  301. else if (tag == 0x00280011)
  302. {
  303. nRet = fseek(f, -2, SEEK_CUR);
  304. if (nRet)
  305. {
  306. FERROR("fseek error! offset:{$}", ftell(f));
  307. }
  308. width = getShort16(f, isLitteEndian);
  309. FINFO("tag == 0x00280011 width == {$}", width);
  310. }
  311. else if (tag == 0x00280100)
  312. {
  313. nRet = fseek(f, -2, SEEK_CUR);
  314. if (nRet)
  315. {
  316. FERROR("fseek error! offset:{$}", ftell(f));
  317. }
  318. nBitsAllocated = getShort16(f, isLitteEndian);
  319. FINFO("tag == 0x00280100 nBitsAllocated == {$}", nBitsAllocated);
  320. }
  321. else if (tag == 0x00280102)
  322. {
  323. nRet = fseek(f, -2, SEEK_CUR);
  324. if (nRet)
  325. {
  326. FERROR("fseek error! offset:{$}", ftell(f));
  327. }
  328. nHighBit = getShort16(f, isLitteEndian);
  329. if (nHighBit == 0)
  330. nHighBit = nBitsAllocated - 1;
  331. FINFO("tag == 0x00280102 nHighBit == {$}", nHighBit);
  332. }
  333. else if (tag == 0x00280103)
  334. {
  335. bSigned = str2int(VF) == 0 ? false : true;
  336. FINFO("tag == 0x00280103 VF == {$}, bSigned:{$}", VF, bSigned);
  337. }
  338. else if (tag == 0x00281050)
  339. {
  340. windowCenter = str2int(VF);
  341. FINFO("tag == 0x00281050 VF == {$}, windowCenter:{$}", VF, windowCenter);
  342. }
  343. else if (tag == 0x00281051)
  344. {
  345. windowWidth = str2int(VF);
  346. FINFO("tag == 0x00281051 VF == {$}, windowWidth:{$}", VF, windowWidth);
  347. }
  348. else if (tag == 0x00281052)
  349. {
  350. rescaleIntercept = str2double(VF);
  351. FINFO("tag == 0x00281052 VF == {$}, rescaleIntercept:{$}", VF, rescaleIntercept);
  352. }
  353. else if (tag == 0x00281053)
  354. {
  355. rescaleSlope = str2double(VF);
  356. FINFO("tag == 0x00281053 VF == {$}, rescaleSlope:{$}", VF, rescaleSlope);
  357. }
  358. }
  359. } // end level == 0
  360. }
  361. } // while(decodingTags)
  362. fclose(f);
  363. nWidth = width, nHeight = height, nBits = nHighBit + 1, nWindowCenter = windowCenter, nWindowWidth = windowWidth;
  364. FINFO("load dcm over");
  365. return true;
  366. }
  367. int getTag(FILE* f, bool isLitteEndian) {
  368. short groupWord = getShort16(f, isLitteEndian);
  369. short elementWord = getShort16(f, isLitteEndian);
  370. int tag = groupWord << 16 | elementWord;
  371. return tag;
  372. }
  373. std::string getVR(FILE* f) {
  374. return getString(f, 2);
  375. }
  376. char getByte8(FILE* f) {
  377. char b;
  378. fread(&b, sizeof(char), 1, f);
  379. return b;
  380. }
  381. short getShort16(FILE* f, bool isLitteEndian) {
  382. char b0 = getByte8(f);
  383. char b1 = getByte8(f);
  384. if (isLitteEndian)
  385. return (((b1 << 8) & 0x0000ff00) + (b0 & 0x000000ff));
  386. else
  387. return (((b0 << 8) & 0x0000ff00) + (b1 & 0x000000ff));
  388. }
  389. int getInt32(FILE* f, bool isLitteEndian) {
  390. char b0 = getByte8(f);
  391. char b1 = getByte8(f);
  392. char b2 = getByte8(f);
  393. char b3 = getByte8(f);
  394. if (isLitteEndian)
  395. return ((b3 << 24) + ((b2 << 16) & 0x00ff0000) + ((b1 << 8) & 0x0000ff00) + (b0 & 0x000000ff));
  396. else
  397. return ((b0 << 24) + ((b1 << 16) & 0x00ff0000) + ((b2 << 8) & 0x0000ff00) + (b3 & 0x000000ff));
  398. }
  399. std::string getString(FILE* f, int length) {
  400. if (length <= 0 || length > 65536 * 4) return std::string("");
  401. char* buf = new char[length + 1];
  402. size_t pos = 0;
  403. while (pos < length)
  404. {
  405. size_t count = fread(buf + pos, sizeof(char), (length - pos), f);
  406. if (count <= 0) break;
  407. pos += count;
  408. }
  409. buf[length] = '\0';
  410. std::string str(buf);
  411. delete[] buf;
  412. return str;
  413. }
  414. int str2int(std::string s) {
  415. std::stringstream sstr(s);
  416. int num = 0;
  417. sstr >> num;
  418. return num;
  419. }
  420. double str2double(std::string s) {
  421. std::stringstream sstr(s);
  422. double num = 0;
  423. sstr >> num;
  424. return num;
  425. }
  426. std::string getInExplicitVR(int tag)
  427. {
  428. switch (tag)
  429. {
  430. case 0x00020000://文件元信息长度
  431. return "UL";
  432. break;
  433. case 0x00020010://传输语法
  434. return "UI";
  435. break;
  436. case 0x00020013://文件生成程序的标题
  437. return "SH";
  438. break;
  439. case 0x00080005://文本编码
  440. return "CS";
  441. break;
  442. case 0x00080008:
  443. return "CS";
  444. break;
  445. case 0x00081032://成像时间
  446. return "SQ";
  447. break;
  448. case 0x00081111:
  449. return "SQ";
  450. break;
  451. case 0x00080020://检查日期
  452. return "DA";
  453. break;
  454. case 0x00080060://成像仪器
  455. return "CS";
  456. break;
  457. case 0x00080070://成像仪厂商
  458. return "LO";
  459. break;
  460. case 0x00080080:
  461. return "LO";
  462. break;
  463. case 0x00100010://病人姓名
  464. return "PN";
  465. break;
  466. case 0x00100020://病人id
  467. return "LO";
  468. break;
  469. case 0x00100030://病人生日
  470. return "DA";
  471. break;
  472. case 0x00180060://电压
  473. return "DS";
  474. break;
  475. case 0x00181030://协议名
  476. return "LO";
  477. break;
  478. case 0x00181151:
  479. return "IS";
  480. break;
  481. case 0x00200010://检查ID
  482. return "SH";
  483. break;
  484. case 0x00200011://序列
  485. return "IS";
  486. break;
  487. case 0x00200012://成像编号
  488. return "IS";
  489. break;
  490. case 0x00200013://影像编号
  491. return "IS";
  492. break;
  493. case 0x00280002://像素采样1为灰度3为彩色
  494. return "US";
  495. break;
  496. case 0x00280004://图像模式MONOCHROME2为灰度
  497. return "CS";
  498. break;
  499. case 0x00280006://颜色值排列顺序 可能骨头重建那个的显示就是这个问题
  500. return "US";
  501. break;
  502. case 0x00280008://图像的帧数
  503. return "IS";
  504. break;
  505. case 0x00280010://row高
  506. return "US";
  507. break;
  508. case 0x00280011://col宽
  509. return "US";
  510. break;
  511. case 0x00280100://单个采样数据长度
  512. return "US";
  513. break;
  514. case 0x00280101://实际长度
  515. return "US";
  516. break;
  517. case 0x00280102://采样最大值
  518. return "US";
  519. break;
  520. case 0x00280103://像素数据类型
  521. return "US";
  522. break;
  523. case 0x00281050://窗位
  524. return "DS";
  525. break;
  526. case 0x00281051://窗宽
  527. return "DS";
  528. break;
  529. case 0x00281052:
  530. return "DS";
  531. break;
  532. case 0x00281053:
  533. return "DS";
  534. break;
  535. case 0x00400008://文件夹标签
  536. return "SQ";
  537. break;
  538. case 0x00400260://文件夹标签
  539. return "SQ";
  540. break;
  541. case 0x00400275://文件夹标签
  542. return "SQ";
  543. break;
  544. case 0x7fe00010://像素数据开始处
  545. return "OW";
  546. break;
  547. default:
  548. return "UN";
  549. break;
  550. }
  551. }
  552. };