DICOM.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | DICOM PHP Libraries |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 2007-2010 Alberto Bulletti |
  7. // +----------------------------------------------------------------------+
  8. // | |
  9. // +----------------------------------------------------------------------+
  10. // | Author: Alberto Bulletti |
  11. // +----------------------------------------------------------------------+
  12. //
  13. // $Id: DICOM.php,v 1.9.3 2010/02/01 19:18:17
  14. /*
  15. * Contributions to version 0.3 by PDicom
  16. */
  17. require_once('PEAR.php');
  18. require_once('DICOM/Element.php');
  19. require_once('DICOM/Dictionary.php');
  20. $dictionary = new File_DICOM_Dictionary();
  21. define('FILE_DICOM_VR_TYPE_EXPLICIT_32_BITS', 0);
  22. define('FILE_DICOM_VR_TYPE_EXPLICIT_16_BITS', 1);
  23. define('FILE_DICOM_VR_TYPE_IMPLICIT', 2);
  24. define('IMPLICIT_VR_LITTLE_ENDIAN',"1.2.840.10008.1.2");
  25. define('EXPLICIT_VR_LITTLE_ENDIAN',"1.2.840.10008.1.2.1");
  26. define('EXPLICIT_VR_BIG_ENDIAN',"1.2.840.10008.1.2.2");
  27. $fp = fopen('data.txt', 'w');
  28. fwrite($fp, 'HERE WE R 7');
  29. fclose($fp);
  30. $VR_array = array(
  31. 'AE' => array('Application Entity',16,0),
  32. 'AS' => array('Age String',4,1),
  33. 'AT' => array('Attribute Tag',4,1),
  34. 'CS' => array('Code String',16,0),
  35. 'DA' => array('Date',8,1),
  36. 'DS' => array('Decimal String',16,0),
  37. 'DT' => array('Date Time',26,0),
  38. 'FL' => array('Floating Point Single',4,1),
  39. 'FD' => array('Floating Point Double',8,1),
  40. 'IS' => array('Integer String',12,0),
  41. 'LO' => array('Long String',64,0),
  42. 'LT' => array('Long Text',10240,0),
  43. 'OB' => array('Other Byte String',0,0),
  44. 'OX' => array('Mixed. Other {Byte|Word} String',0,0),
  45. 'OW' => array('Other Word String',0,0),
  46. 'PN' => array('Person Name',64,0),
  47. 'SH' => array('Short String',16,0),
  48. 'SL' => array('Signed Long',4,1),
  49. 'SQ' => array('Sequence of Items',0,0),
  50. 'SS' => array('Signed Short',2,1),
  51. 'ST' => array('Short Text',1024,0),
  52. 'TM' => array('Time',16,0),
  53. 'UI' => array('Unique Identifier UID',64,0),
  54. 'UL' => array('Unsigned Long',4,1),
  55. 'UN' => array('Unknown',0,0),
  56. 'US' => array('Unsigned Short',2,1),
  57. 'UT' => array('Unlimited Text',0,0)
  58. );
  59. /**
  60. * This class allows reading and modifying of DICOM files
  61. */
  62. class File_DICOM extends PEAR
  63. {
  64. /**
  65. * DICOM dictionary.
  66. * @var array
  67. */
  68. var $dict;
  69. /**
  70. * Flag indicating if the current file is a DICM file or a NEMA file.
  71. * true => DICM, false => NEMA.
  72. *
  73. * @var bool
  74. */
  75. var $is_dicm;
  76. /**
  77. * Currently open file.
  78. * @var string
  79. */
  80. var $current_file;
  81. /**
  82. * Initial 0x80 bytes of last read file
  83. * @var string
  84. */
  85. var $_preamble_buff;
  86. /**
  87. * Array of DICOM elements indexed by group and element index
  88. * @var array
  89. */
  90. var $_elements;
  91. /**
  92. * Array of DICOM elements indexed by name
  93. * @var array
  94. */
  95. var $_elements_by_name;
  96. /**
  97. * Transfer Syntax. To be able of reading different types according to standard
  98. * 1.2.840.10008.1.2 Implicit VR, Little Endian
  99. * 1.2.840.10008.1.2.1 Explicit VR, Little Endian
  100. * 1.2.840.10008.1.2.2 Explicit VR, Big Endian
  101. * @var string
  102. */
  103. var $transfer_syntax;
  104. /**
  105. * Encapsulated Transfer Syntax.
  106. * 1.2.840.10008.1.2.4.50 JPEG baseline
  107. * 1.2.840.10008.1.2.4.51 JPEG extended
  108. * 1.2.840.10008.1.2.4.57 JPEG lossless, non-hierarchical
  109. * 1.2.840.10008.1.2.4.70 JPEG lossless, non-hierarchical, first-order prediction
  110. * @var string
  111. */
  112. var $_encapsulated_transfer_syntax = array(
  113. "1.2.840.10008.1.2.4.50",
  114. "1.2.840.10008.1.2.4.51",
  115. "1.2.840.10008.1.2.4.57",
  116. "1.2.840.10008.1.2.4.70"
  117. );
  118. /**
  119. * Constructor.
  120. * It creates a File_DICOM object.
  121. *
  122. * @access public
  123. */
  124. function File_DICOM()
  125. {
  126. /**
  127. * Initialize dictionary.
  128. */
  129. global $dictionary;
  130. $this->dict = $dictionary->dict;
  131. }
  132. /**
  133. * Utility to know if a file is DICOM or not.
  134. * It is not a bullet-proof test.
  135. *
  136. * @access public
  137. * @param string $infile The DICOM file to parse
  138. * @return boolean true if file is DICOM, or false is file does not satisfy the preamble test.
  139. * Notice, that a file returning false could still be DICOM.
  140. */
  141. function isDicom($infile) {
  142. return $this->parse($infile, true);
  143. }
  144. /**
  145. * Parse a DICOM file
  146. * Parse a DICOM file and get all of its header members
  147. *
  148. * @access public
  149. * @param string $infile The DICOM file to parse
  150. * @param boolean $only_check_type Default to false. It is true when called by method isDicom
  151. * @return mixed true on success or DICOM file, PEAR_Error on failure, or false when is not a DICOM file
  152. */
  153. function parse($infile, $only_check_type = false)
  154. {
  155. $this->current_file = $infile;
  156. // Fill the transfer syntax by default to Explicit VR Little Endian
  157. $this->transfer_syntax = EXPLICIT_VR_LITTLE_ENDIAN;
  158. $fh = @fopen($infile, "rb");
  159. if (!$fh) {
  160. return $this->raiseError("Could not open file $infile for reading");
  161. }
  162. $stat = fstat($fh);
  163. $this->_file_length = $stat[7];
  164. // Test for NEMA or DICOM file.
  165. // If DICM, store initial preamble and leave file ptr at 0x84.
  166. $this->_preamble_buff = fread($fh, 0x80);
  167. $buff = fread($fh, 4);
  168. $this->is_dicm = ($buff == 'DICM');
  169. if ($only_check_type) {
  170. fclose($fh);
  171. return $this->is_dicm;
  172. }
  173. if (!$this->is_dicm) {
  174. fseek($fh, 0);
  175. }
  176. $last_group = 0x0000;
  177. // Fill in hash with header members from given file.
  178. while (ftell($fh) < $this->_file_length)
  179. {
  180. $element =& new File_DICOM_Element($fh, $this->dict,$this->transfer_syntax,$last_group);
  181. $this->_elements[$element->group][$element->element][] =& $element;
  182. $this->_elements_by_name[$element->name][] =& $element;
  183. $last_group = $element->group;
  184. // Fill the proper transfer syntax
  185. if ($element->group == 0x0002 && $element->element == 0x0010) {
  186. $this->transfer_syntax = $element->value;
  187. }
  188. }
  189. fclose($fh);
  190. return true;
  191. }
  192. /**
  193. * Write current contents to a DICOM file.
  194. *
  195. * @access public
  196. * @param string $outfile The name of the file to write. If not given
  197. * it assumes the name of the file parsed.
  198. * If no file was parsed and no name is given
  199. * returns a PEAR_Error
  200. * @return mixed true on success, PEAR_Error on failure
  201. */
  202. function write($outfile = '')
  203. {
  204. if ($outfile == '') {
  205. if (isset($this->current_file)) {
  206. $outfile = $this->current_file;
  207. } else {
  208. return $this->raiseError("File name not given (and no file currently open)");
  209. }
  210. }
  211. $fh = @fopen($outfile, "wb");
  212. if (!$fh) {
  213. return $this->raiseError("Could not open file $outfile for writing");
  214. }
  215. // Writing file from scratch will always fail for now
  216. if (!isset($this->_preamble_buff)) {
  217. return $this->raiseError("Cannot write DICOM file from scratch");
  218. }
  219. // Don't store initial preamble and DICM word. Working with NEMA.
  220. //fwrite($fh, $this->_preamble_buff);
  221. //fwrite($fh, 'DICM');
  222. /*$buff = fread($fh, 4);
  223. $this->is_dicm = ($buff == 'DICM');
  224. if (!$this->is_dicm) {
  225. fseek($fh, 0);
  226. }*/
  227. // There are not that much groups/elements. Using foreach()
  228. foreach (array_keys($this->_elements) as $group) {
  229. foreach (array_keys($this->_elements[$group]) as $element) {
  230. foreach (array_keys($this->_elements[$group][$element]) as $z) {
  231. fwrite($fh, pack('v', $group));
  232. fwrite($fh, pack('v', $element));
  233. $code = $this->_elements[$group][$element][$z]->code;
  234. // Preserve the VR type from the file parsed
  235. if (($this->_elements[$group][$element][$z]->vr_type == FILE_DICOM_VR_TYPE_EXPLICIT_32_BITS) or ($this->_elements[$group][$element][$z]->vr_type == FILE_DICOM_VR_TYPE_EXPLICIT_16_BITS)) {
  236. fwrite($fh, pack('CC', $code{0}, $code{1}));
  237. // This is an OB, OW, SQ, UN or UT: 32 bit VL field.
  238. if ($this->_elements[$group][$element][$z]->vr_type == FILE_DICOM_VR_TYPE_EXPLICIT_32_BITS) {
  239. fwrite($fh, pack('V', $this->_elements[$group][$element][$z]->length));
  240. } else { // not using fixed length from VR!!!
  241. fwrite($fh, pack('v', $this->_elements[$group][$element][$z]->length));
  242. }
  243. } elseif ($this->_elements[$group][$element][$z]->vr_type == FILE_DICOM_VR_TYPE_IMPLICIT) {
  244. fwrite($fh, pack('V', $this->_elements[$group][$element][$z]->length));
  245. }
  246. switch ($code) {
  247. // Decode unsigned longs and shorts.
  248. case 'UL':
  249. fwrite($fh, pack('V', $this->_elements[$group][$element][$z]->value));
  250. break;
  251. case 'US':
  252. fwrite($fh, pack('v', $this->_elements[$group][$element][$z]->value));
  253. break;
  254. // Floats: Single and double precision.
  255. case 'FL':
  256. fwrite($fh, pack('f', $this->_elements[$group][$element][$z]->value));
  257. break;
  258. case 'FD':
  259. fwrite($fh, pack('d', $this->_elements[$group][$element][$z]->value));
  260. break;
  261. // Binary data. Only save position. Is this right?
  262. case 'OW':
  263. case 'OB':
  264. case 'OX':
  265. // Binary data. Value only contains position on the parsed file.
  266. // Will fail when file name for writing is the same as for parsing.
  267. $fh2 = @fopen($this->current_file, "rb");
  268. if (!$fh2) {
  269. return $this->raiseError("Could not open file {$this->current_file} for reading");
  270. }
  271. fseek($fh2, $this->_elements[$group][$element][$z]->value);
  272. fwrite($fh, fread($fh2, $this->_elements[$group][$element][$z]->length));
  273. fclose($fh2);
  274. break;
  275. default:
  276. fwrite($fh, $this->_elements[$group][$element][$z]->value, $this->_elements[$group][$element][$z]->length);
  277. break;
  278. }
  279. }
  280. }
  281. }
  282. fclose($fh);
  283. return true;
  284. }
  285. /**
  286. * Gets the value for a DICOM element
  287. * Gets the value for a DICOM element of a given group from the
  288. * parsed DICOM file.
  289. *
  290. * @access public
  291. * @param mixed $gp_or_name The group the DICOM element belongs to
  292. * (integer), or it's name (string)
  293. * @param integer $el The identifier for the DICOM element
  294. * (unique inside a group)
  295. * @param integer $z The 3rd dimension for the element (created for sequences)
  296. * By default is set to 0 for backwards compatibility
  297. * (usually for single elements).
  298. * @return mixed The value for the DICOM element on success, PEAR_Error on failure
  299. */
  300. function getValue($gp_or_name, $el = null, $z = 0)
  301. {
  302. if (isset($el)) // retreive by group and element index
  303. {
  304. if (isset($this->_elements[$gp_or_name][$el][$z])) {
  305. $value = $this->_elements[$gp_or_name][$el][$z]->getValue();
  306. if (empty($value)) {
  307. return null;
  308. }
  309. return $this->_elements[$gp_or_name][$el][$z]->getValue();
  310. }
  311. else {
  312. $this->raiseError("Element ($gp_or_name,$el) not found");
  313. return null;
  314. }
  315. }
  316. else // retrieve by name
  317. {
  318. if (isset($this->_elements_by_name[$gp_or_name][$z])) {
  319. $value = $this->_elements_by_name[$gp_or_name][$z]->getValue();
  320. if (empty($value)) {
  321. return null;
  322. }
  323. return $this->_elements_by_name[$gp_or_name][$z]->getValue();
  324. }
  325. else {
  326. $this->raiseError("Element $gp_or_name not found");
  327. return null;
  328. }
  329. }
  330. }
  331. /**
  332. * Sets the value for a DICOM element
  333. * Only works with strings now.
  334. *
  335. * @access public
  336. * @param integer $gp The group the DICOM element belongs to
  337. * @param integer $el The identifier for the DICOM element (unique inside a group)
  338. * @param integer $z The index of the element. By default 0 (for single elements)
  339. */
  340. function setValue($gp, $el, $value, $z = 0)
  341. {
  342. $this->_elements[$gp][$el][$z]->value = $value;
  343. $this->_elements[$gp][$el][$z]->length = strlen($value);
  344. }
  345. /**
  346. * Dumps the contents of the image inside the DICOM file
  347. * (element 0x0010 from group 0x7FE0) to a PGM (Portable Gray Map) file.
  348. * Use with Caution!!. For a 8.5MB DICOM file on a P4 it takes 28
  349. * seconds to dump it's image.
  350. *
  351. * @access public
  352. * @param string $filename The file where to save the image
  353. * @return mixed true on success, PEAR_Error on failure.
  354. */
  355. function dumpImage($filename)
  356. {
  357. $length = $this->_elements[0x7FE0][0x0010]->length;
  358. $rows = $this->getValue(0x0028,0x0010);
  359. $cols = $this->getValue(0x0028,0x0011);
  360. $fh = @fopen($filename, "wb");
  361. if (!$fh) {
  362. return $this->raiseError("Could not open file $filename for writing");
  363. }
  364. // magick word
  365. fwrite($fh, "P5\n");
  366. // comment
  367. fwrite($fh, "# file generated by PEAR::File_DICOM on ".strftime("%Y-%m-%d", time())." \n");
  368. fwrite($fh, "# do not use for diagnosing purposes\n");
  369. fwrite($fh, "$cols $rows\n");
  370. // always 255 grays
  371. fwrite($fh, "255\n");
  372. $pos = $this->getValue(0x7FE0,0x0010);
  373. $fh_in = @fopen($this->current_file, "rb");
  374. if (!$fh_in) {
  375. return $this->raiseError("Could not open file {$this->current_file} for reading");
  376. }
  377. fseek($fh_in, $pos);
  378. $block_size = 4096;
  379. $blocks = ceil($length / $block_size);
  380. for ($i = 0; $i < $blocks; $i++) {
  381. if ($i == $blocks - 1) { // last block
  382. $chunk_length = ($length % $block_size) ? ($length % $block_size) : $block_size;
  383. } else {
  384. $chunk_length = $block_size;
  385. }
  386. $chunk = fread($fh_in, $chunk_length);
  387. $pgm = '';
  388. $half_chunk_length = $chunk_length/2;
  389. $rr = unpack("v$half_chunk_length", $chunk);
  390. for ($j = 1; $j <= $half_chunk_length; $j++) {
  391. $pgm .= pack('C', $rr[$j] >> 4);
  392. }
  393. fwrite($fh, $pgm);
  394. }
  395. fclose($fh_in);
  396. fclose($fh);
  397. return true;
  398. }
  399. /**
  400. * Creates an array of GD image object.
  401. *
  402. * @access public
  403. * @param string $filename The file where to save the image
  404. * @return mixed image object array on success,
  405. * PEAR_Error on failure or NULL if GD is not configured.
  406. */
  407. function imagecreatefromDICOM($filename, $window = null, $level = null)
  408. {
  409. if (!function_exists('imagecreatetruecolor')) {
  410. return NULL;
  411. }
  412. // Let's read some values from DICOM file
  413. $length = $this->_elements[0x7FE0][0x0010][0]->length;
  414. $rows = $this->getValue(0x0028, 0x0010);
  415. $cols = $this->getValue(0x0028, 0x0011);
  416. $samples_per_pixel = $this->getValue(0x0028, 0x0002);
  417. $bits = $this->getValue(0x0028, 0x0100);
  418. $high_bit = $this->getValue(0x0028, 0x0102);
  419. $dose_scaling = (empty($this->_elements[0x3004][0x000E][0]->value))? 1 : $this->getValue(0x3004, 0x000E);
  420. $window_center = (empty($this->_elements[0x0028][0x1050][0]->value))? 0 : $this->getValue(0x0028, 0x1050);
  421. $window_width = (empty($this->_elements[0x0028][0x1051][0]->value))? 0 : $this->getValue(0x0028, 0x1051);
  422. $rescale_intercept = (empty($this->_elements[0x0028][0x1052][0]->value))? 0 : $this->getValue(0x0028, 0x1052);
  423. $rescale_slope = (empty($this->_elements[0x0028][0x1053][0]->value))? 1 : $this->getValue(0x0028, 0x1053);
  424. $number_of_frames = (empty($this->_elements[0x0028][0x0008][0]->value))? 1 : $this->getValue(0x0028, 0x0008);
  425. $pixel_representation = $this->getValue(0x0028, 0x0103);
  426. $photometric_interpretation = $this->getValue(0x0028, 0x0004);
  427. $starting_position = $this->getValue(0x7FE0, 0x0010);
  428. $transfer_syntax_uid = $this->getValue(0x0002, 0x0010);
  429. //$this->_elements = null;
  430. if (in_array($transfer_syntax_uid, $this->_encapsulated_transfer_syntax)) {
  431. // Sorry, encapsulated data is more complex to display... by now.
  432. return NULL;
  433. }
  434. if ($rows * $cols >= 500000) {
  435. // Sorry, file is too big, conver it in a different way
  436. return NULL;
  437. }
  438. // Window Center and Width can have multiple values. By now, just reading the first one. It assumes the delimiter
  439. // is the "\"
  440. if (!(strpos($window_center,"\\") === false)) {
  441. $temp = explode("\\",$window_center);
  442. $window_center = intval($temp[0]);
  443. }
  444. if (!(strpos($window_width,"\\") === false)) {
  445. $temp = explode("\\",$window_width);
  446. $window_width = intval($temp[0]);
  447. }
  448. // Opening the file
  449. $fh = fopen ($filename, 'rb');
  450. if (!$fh) {
  451. return $this->raiseError("Could not open file $filename for writing");
  452. }
  453. fseek($fh, $starting_position);
  454. // $data holds the pixel values
  455. $maximum = array();
  456. $minimum = array();
  457. $images = array();
  458. $data = array();
  459. $max = array();
  460. $min = array();
  461. $current_position = $starting_position;
  462. $current_image = 0;
  463. $bytes_to_read = $bits/8;
  464. $size_image = $cols * $rows * $bytes_to_read * $samples_per_pixel;
  465. $length = $size_image * $number_of_frames;
  466. while (ftell($fh) < $starting_position + $length) {
  467. if ($current_position == $starting_position + $current_image*$size_image) {
  468. $x = 0;
  469. $y = 0;
  470. for ($i = 0; $i < $samples_per_pixel; $i++) {
  471. $max[$current_image][$i] = -20000; // Small enough so it will be properly calculated
  472. $min[$current_image][$i] = 20000; // Large enough so it wil be properly calculated
  473. }
  474. $current_image++;
  475. }
  476. for ($i = 0; $i < $samples_per_pixel; $i++) {
  477. $chunk = fread($fh, $bytes_to_read);
  478. $current_position += $bytes_to_read;
  479. $pixel_value = base_convert(bin2hex(strrev($chunk)), 16, 10);
  480. // Now we have the pixel value
  481. // Checking if 2's complement
  482. $pixel_value = ($pixel_representation)? $this->complement2($pixel_value, $high_bit) : $pixel_value;
  483. // Getting the right value according to slope and intercept
  484. $pixel_value = $pixel_value*$rescale_slope + $rescale_intercept;
  485. // Multiplying for dose_grid_scaling
  486. $pixel_value = $pixel_value*$dose_scaling;
  487. // Assigning the value
  488. $data[$current_image - 1][$x][$y][$i] = $pixel_value;
  489. // Getting the max
  490. if ($pixel_value > $max[$current_image - 1][$i]) {
  491. $max[$current_image - 1][$i] = $pixel_value; // max
  492. }
  493. // Getting the min
  494. if ($pixel_value < $min[$current_image - 1][$i]) {
  495. $min[$current_image - 1][$i] = $pixel_value; // min
  496. }
  497. }
  498. $y++;
  499. if ($y == $cols) { // Next row
  500. $x++;
  501. $y=0;
  502. }
  503. }
  504. fclose ( $fh );
  505. for ($index = 0; $index < $current_image; $index++) {
  506. for ($i = 0; $i < $samples_per_pixel; $i++) {
  507. // Real max and min according to window center & width (if set)
  508. $maximum[$i] = ($window_center != 0 && $window_width != 0)? round($window_center + $window_width/2) : $max[$index][$i];
  509. $minimum[$i] = ($window_center != 0 && $window_width != 0)? round($window_center - $window_width/2) : $min[$index][$i];
  510. // Check if window and level are sent
  511. $maximum[$i] = (!empty($window) && !empty($level))? round($level + $window/2) : $maximum[$i];
  512. $minimum[$i] = (!empty($window) && !empty($level))? round($level - $window/2) : $minimum[$i];
  513. //echo $index . " " . $maximum . " " . $minimum . "<br />\n";
  514. if ($maximum[$i] == $minimum[$i]) { // Something wrong. Avoid having a zero division
  515. return NULL;
  516. }
  517. }
  518. $img = imagecreatetruecolor($cols, $rows);
  519. for($x =0; $x < $rows;$x++) {
  520. for ($y = 0; $y < $cols; $y++) {
  521. $pixel_values = array();
  522. for ($i = 0; $i < $samples_per_pixel; $i++) {
  523. $pixel_value = $data[$index][$x][$y][$i];
  524. // truncating pixel values over max and below min
  525. $pixel_value = ($pixel_value > $maximum[$i])? $maximum[$i] : $pixel_value;
  526. $pixel_value = ($pixel_value < $minimum[$i])? $minimum[$i] : $pixel_value;
  527. // Converting to gray value
  528. $pixel_value = ($pixel_value - $minimum[$i])/($maximum[$i] - $minimum[$i])*255;
  529. // For MONOCHROME1 we have to invert the pixel values.
  530. if ($photometric_interpretation == "MONOCHROME1") {
  531. $pixel_value = 255 - $pixel_value;
  532. }
  533. $pixel_values[$i] = $pixel_value;
  534. }
  535. if ($samples_per_pixel == 1) {
  536. $color = imagecolorallocate($img, $pixel_values[0], $pixel_values[0], $pixel_values[0]);
  537. }
  538. else {
  539. $color = imagecolorallocate($img, $pixel_values[0], $pixel_values[1], $pixel_values[2]);
  540. }
  541. imagesetpixel($img, $y, $x, $color);
  542. }
  543. }
  544. $images[] = $img;
  545. }
  546. return $images;
  547. }
  548. /**
  549. * Return an integer that was saved as complement 2 .
  550. *
  551. * @access public
  552. * @param integer $number The integer number to convert
  553. * @param integer $high_bit The high bit for the value. By default 15 (assumes 2 bytes)
  554. * @return integer the number after complement's 2 applied
  555. */
  556. function complement2($number, $high_bit = 15)
  557. {
  558. $sign = $number >> $high_bit;
  559. if ($sign) { // Negative
  560. $number = pow(2, $high_bit + 1) - $number;
  561. $number = -1 * $number;
  562. }
  563. return $number;
  564. }
  565. /**
  566. * Returns an html string with the DICOM file parsed.
  567. *
  568. * @access public
  569. * @param object $dicom_element A Dicom element. It could be a sequence too.
  570. * @param string $prefix To have a nice display at the beginning of each line
  571. * @return string An html string
  572. */
  573. function htmlParse($dicom_element="", $prefix="Element")
  574. {
  575. if ($dicom_element == "") {
  576. $dicom_element = $this;
  577. }
  578. $data = "";
  579. $count = 1;
  580. if (count($dicom_element->_elements) > 0) {
  581. foreach (array_keys($dicom_element->_elements) as $group) { // Loading groups
  582. foreach (array_keys($dicom_element->_elements[$group]) as $element) { // Loading elements
  583. foreach ($dicom_element->_elements[$group][$element] as $thisElement) { // Loading items in element (Sequences or single element)
  584. $value = ($thisElement->name == "UNKNOWN")? "Propriatory": $thisElement->value;
  585. $data .= "<span style=\"color:blue;\"> $prefix.$count - ".$thisElement->name . "</span> <span style=\"color:red;\">[" . $this->strHex($group) . "][" . $this->strHex($element) . "] <strong>(Length: ". $thisElement->length . ")</strong></span> = " . $value . "<br />";
  586. $data .= $this->htmlParse($thisElement,"$prefix.$count");
  587. $count++;
  588. }
  589. }
  590. }
  591. }
  592. return $data;
  593. }
  594. /**
  595. * Returns an string as 0x0000. This might be done also using sprintf or alike but not so familiar with that.
  596. *
  597. * @access public
  598. * @param integer $value An integer value. Either the group or element number.
  599. * @return string $string An string with the format 0x0000.
  600. */
  601. function strHex($value)
  602. {
  603. $string = "0x" . str_pad(strtoupper(dechex($value)), 4, "0", STR_PAD_LEFT);
  604. return $string;
  605. }
  606. }
  607. ?>