123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905 |
- <?php
- /**
- * tools/pixeler.php file
- *
- * @package Nanodicom
- * @category Tools
- * @author Nano Documet <nanodocumet@gmail.com>
- * @version 1.3.1
- * @copyright (c) 2010-2011
- * @license http://www.opensource.org/licenses/mit-license.php MIT-license
- */
- /**
- * Dicom_Pixeler class.
- *
- * Extends Nanodicom. Pixel data reader.
- * Currently support:
- * - Only uncompressed pixel data.
- * - Photometric Representations of: Monochrome1, Monochrome2 and RGB (color-by-plane and color-by-pixel)
- * - Big endian and little endian, explicit and implicit
- * - Pixel Representation: Unsigned Integer and 2's complement
- *
- * @package Nanodicom
- * @category Tools
- * @author Nano Documet <nanodocumet@gmail.com>
- * @version 1.3.1
- * @copyright (c) 2010-2011
- * @license http://www.opensource.org/licenses/mit-license.php MIT-license
- */
- class Dicom_Pixeler extends Nanodicom {
- /**
- * @var string driver to be used: GD (gd), ImageMagick (imagick), GraphicsMagick (gmagick)
- */
- public static $driver = 'gd';
-
- protected $_rows;
- protected $_cols;
- protected $_endian;
- protected $_vr_mode;
- protected $_dose_scaling;
- protected $_rescale_slope;
- protected $_rescale_intercept;
- protected $_samples_per_pixel;
- protected $_pixel_representation;
-
- /**
- * Supported Image Transfer Syntaxes for reading image data
- * indexed by Value of the Transfer Syntax
- * each array contains: Name.
- * @var array
- */
- public static $reading_image_transfer_syntaxes = array(
- // Implicit VR Little Endian "1.2.840.10008.1.2"
- Nanodicom::IMPLICIT_VR_LITTLE_ENDIAN => array(
- 'name' => 'Implicit VR Little Endian'
- ),
- // Explicit VR Little Endian "1.2.840.10008.1.2.1"
- Nanodicom::EXPLICIT_VR_LITTLE_ENDIAN => array(
- 'name' => 'Explicit VR Little Endian'
- ),
- // Explicit VT Big Endian "1.2.840.10008.1.2.2"
- Nanodicom::EXPLICIT_VR_BIG_ENDIAN => array(
- 'name' => 'Explicit VR Big Endian'
- ),
- // DICOM Deflated Little Endian (Explicit VR)
- '1.2.840.10008.1.2.1.99' => array(
- 'name' => 'DICOM Deflated Little Endian (Explicit VR)'
- ),
- // Jpeg Lossy Baseline (1) - 8 bits
- '1.2.840.10008.1.2.4.50' => array(
- 'name' => 'Jpeg Lossy Baseline (1) - 8 bits'
- ),
- // RLE - Run Length Encoding, Lossless
- '1.2.840.10008.1.2.5' => array(
- 'name' => 'RLE - Run Length Encoding, Lossless'
- ),
- );
- /**
- * Public method to set the driver to be used
- *
- * @param string name of the driver
- * @return object the instance to allow chaining
- */
- public function set_driver($driver)
- {
- self::$driver = $driver;
- return $this;
- }
-
- /**
- * Public method to get the driver currently set
- *
- * @return string the current driver set
- */
- public function get_driver()
- {
- return self::$driver;
- }
- /**
- * Public method to add a jpeg lossy 8 bits image
- * Still under development.
- *
- * @return string the name of the jpeg file
- */
- public function add_lossy_jpeg8($filename)
- {
- $dataset = array();
-
- // Get the data blob
- $blob = file_get_contents($filename);
- // Create first item
- $value = array(
- 'len' => 0,
- 'val' => '',
- 'vr' => 'IT',
- '_vr' => 'IT',
- 'bin' => FALSE,
- 'off' => 0, // We don't know the offset
- 'ds' => array(),
- 'done' => TRUE,
- );
- $dataset[0xFFFE][0xE000][] = $value;
-
- // Attach jpeg
- $value = array(
- 'len' => sprintf('%u', strlen($blob)),
- 'val' => $blob,
- 'vr' => 'IT',
- '_vr' => 'IT',
- 'bin' => TRUE,
- 'off' => 0, // We don't know the offset
- 'ds' => array(),
- 'done' => TRUE,
- );
- $dataset[0xFFFE][0xE000][] = $value;
-
- // Pixel Data element
- $value = array(
- 'len' => -1,
- 'val' => '',
- 'vr' => 'OB',
- '_vr' => 'OB',
- 'bin' => FALSE,
- 'off' => 0, // We don't know the offset
- 'ds' => $dataset,
- 'done' => TRUE,
- );
-
- $this->_dataset[0x7FE0][0x0010][0] = $value;
- // Delimeter
- $value = array(
- 'len' => 0,
- 'val' => '',
- 'vr' => 'DI',
- '_vr' => 'DI',
- 'bin' => FALSE,
- 'off' => 0, // We don't know the offset
- 'ds' => array(),
- 'done' => TRUE,
- );
-
- $this->_dataset[0xFFFE][0xE0DD][0] = $value;
-
- $info = getimagesize($filename);
-
- // Set the rows (height of image)
- $this->value(0x0028, 0x0010, (int) $info[1]);
- // Set the columns (width of image)
- $this->value(0x0028, 0x0011, (int) $info[0]);
- // Set the bits allocated
- $this->value(0x0028, 0x0100, 8);
- // Set the bits stored
- $this->value(0x0028, 0x0101, 8);
- // Set the high bit
- $this->value(0x0028, 0x0102, 7);
- // Set the samples per pixel
- $this->value(0x0028, 0x0002, 1);
- // Set the Photometric Interpretation
- $this->value(0x0028, 0x0004, 'MONOCHROME2');
- // TransferSyntaxUID. JPEG Lossy Baseline (1) - 8 bits
- $this->value(0x0002, 0x0010, '1.2.840.10008.1.2.4.50');
-
- return $this;
- }
-
- /**
- * Public method to get the images from the dicom object
- *
- * @param integer a default window width
- * @param integer a default window center
- * @return mixed false if something is missing or not image data found, otherwise an
- * array of GD objects
- */
- public function get_images($width = NULL, $center = NULL)
- {
- // Parse the object if not parsed yet
- $this->parse();
- // Set the profiler
- $this->profiler['pixel']['start'] = microtime(TRUE);
- $return = $this->_check_driver();
- // If FALSE, driver not found
- if ( ! $return)
- return FALSE;
- // Supported transfer syntaxes
- if ( ! array_key_exists(trim($this->_transfer_syntax), self::$reading_image_transfer_syntaxes))
- {
- // Not supported transfer syntax found
- throw new Nanodicom_Exception('Transfer syntax ":syntax" not supported',
- array(':syntax' => $this->_transfer_syntax), 300);
- }
- // Let's read some values from DICOM file
- $rows = $this->get(0x0028, 0x0010);
- $cols = $this->get(0x0028, 0x0011);
- if ($rows == NULL OR $cols === NULL)
- {
- // There is no rows, no samples per pixel, no pixel data or malformed dicom file
- throw new Nanodicom_Exception('There is no rows, no samples per pixel, no pixel data or malformed dicom file', NULL, 301);
- }
-
- $samples_per_pixel = $this->get(0x0028, 0x0002, 1);
- $bits_allocated = $this->get(0x0028, 0x0100);
- $bits_stored = $this->get(0x0028, 0x0101);
- $high_bit = $this->get(0x0028, 0x0102);
- $dose_scaling = $this->get(0x3004, 0x000E, 1);
- $window_width = ($width == NULL) ? $this->get(0x0028,0x1051, 0) : $width;
- $window_center = ($center == NULL) ? $this->get(0x0028,0x1050, 0) : $center;
- $rescale_intercept = $this->get(0x0028,0x1052, 0);
- $rescale_slope = $this->get(0x0028,0x1053, 1);
- $number_of_frames = (int) $this->get(0x0028,0x0008, 1);
- $pixel_representation = $this->get(0x0028,0x0103);
- $photometric_interpretation = trim($this->get(0x0028,0x0004, 'NONE'));
- $planar_configuration = $this->get(0x0028,0x0006, 0);
- $transfer_syntax_uid = trim($this->get(0x0002,0x0010));
- $blob = $this->get(0x7FE0,0x0010);
- // Save some values for internal use
- // TODO: improve this, probably using $this->pixeler[]?
- $this->_rows = $rows;
- $this->_cols = $cols;
- $this->_dose_scaling = $dose_scaling;
- $this->_rescale_slope = $rescale_slope;
- $this->_rescale_intercept = $rescale_intercept;
- $this->_samples_per_pixel = $samples_per_pixel;
- $this->_pixel_representation = $pixel_representation;
-
- // Window Center and Width can have multiple values. By now, just reading the first one.
- // It assumes the delimiter is the "\"
- if ( ! (strpos($window_center, "\\") === FALSE))
- {
- $temp = explode("\\", $window_center);
- $window_center = (int) $temp[0];
- }
-
- if ( ! (strpos($window_width, "\\") === FALSE))
- {
- $temp = explode("\\", $window_width);
- $window_width = (int) $temp[0];
- }
- // Setting some values
- $images = array();
- $max = array();
- $min = array();
-
- $current_position = $starting_position = 0;
- $current_image = 0;
- $bytes_to_read = (int) $bits_allocated/8;
- $image_size = $cols*$rows*$samples_per_pixel*$bytes_to_read;
- list($vr_mode, $endian) = Nanodicom::decode_transfer_syntax($transfer_syntax_uid);
- $this->_vr_mode = $vr_mode;
- $this->_endian = $endian;
- if ($transfer_syntax_uid == '1.2.840.10008.1.2.4.50')
- {
- // This is jpeg lossy 8-bits. Just get the data.
- $images = array();
- $counter = 0;
-
- foreach ($blob as $group => $elements)
- {
- foreach ($elements as $element => $indexes)
- {
- if ($element == Nanodicom::SEQUENCE_DELIMITER)
- continue;
-
- foreach ($indexes as $values)
- {
- $data = $values;
- if ($counter == 0)
- {
- // Read Basic Offset Table
- }
- else
- {
- // It is a real Item
- if ( ! isset($data['done']))
- {
- // Read value if not read yet
- $this->_read_value_from_blob($data, $group, $element);
- }
- $images[] = $this->_read_image_blob($data['val']);
- }
- $counter++;
- }
- }
- }
-
- return $images;
- }
-
- if ($transfer_syntax_uid == '1.2.840.10008.1.2.5')
- {
- // It is "RLE - Run Length Encoding, Lossless"
- $counter = 0;
- $temp = array();
- foreach ($blob as $group => $elements)
- {
- foreach ($elements as $element => $indexes)
- {
- if ($element == Nanodicom::SEQUENCE_DELIMITER)
- continue;
-
- foreach ($indexes as $values)
- {
- $data = $values;
- $data['g'] = $group;
- $data['e'] = $element;
- if ($counter == 0)
- {
- // Read Basic Offset Table
- }
- else
- {
- // It is a real Item
- $dir = realpath(dirname($this->_location)).DIRECTORY_SEPARATOR;
- $file = basename($this->_location);
- if ( ! isset($data['done']))
- {
- // Read value if not read yet
- $this->_read_value_from_blob($data, $group, $element);
- }
-
- // This is the item data
- $item = $data['val'];
-
- // Save the header
- $header = array();
- // 16 Unsigned Long values
- for ($i = 0; $i <= 16; $i++)
- {
- $chunk = substr($item, 4*$i, 4);
- $header[] = $this->{Nanodicom::$_read_int}(4, Nanodicom::LITTLE_ENDIAN, 4, Nanodicom::UNSIGNED, $chunk);
- }
-
- $total_segments = array_shift($header);
- $segment_index = 0;
- foreach ($header as $starting_byte_of_segment)
- {
- // This is a segment, do the uncompression
- if ($starting_byte_of_segment == 0)
- // Only process if the segment has a positive starting value
- break;
-
- $size_of_segment = ($header[$segment_index + 1] == 0)
- ? strlen($item) - $starting_byte_of_segment
- : $header[$segment_index + 1] - $starting_byte_of_segment;
-
- $temp[$counter - 1][$segment_index] = '';
- $expected_segment_size = $cols*$rows*$bytes_to_read/$total_segments;
- $bytes_count = $current_segment_size = 0;
- while ($bytes_count < $size_of_segment)
- {
- $tmp = $starting_byte_of_segment + $bytes_count;
- // Read "n"
- $n = substr($item, $starting_byte_of_segment + $bytes_count, 1);
- $n = $this->{Nanodicom::$_read_int}(1, Nanodicom::LITTLE_ENDIAN, 1, Nanodicom::SIGNED, $n);
- // Add 1
- $bytes_count++;
- if ($n >= 0 AND $n <= 127)
- {
- $temp[$counter - 1][$segment_index] .= substr($item, $starting_byte_of_segment + $bytes_count, $n + 1);
- $bytes_count = $bytes_count + $n + 1;
- $current_segment_size = $current_segment_size + $n + 1;
- }
- elseif ($n <= -1 AND $n >= -127)
- {
- $byte = substr($item, $starting_byte_of_segment + $bytes_count, 1);
- $temp[$counter - 1][$segment_index] .= str_repeat($byte, -$n + 1);
- $bytes_count++;
- $current_segment_size = $current_segment_size - $n + 1;
- }
- else
- {
- // Do nothing
- }
- }
- $segment_index++;
- }
- }
- // Increment counter
- $counter++;
- }
- }
- }
- $counter--;
- $blob = '';
- for ($count = 0; $count < $counter; $count++)
- {
- if ($photometric_interpretation == 'RGB')
- {
- if ($planar_configuration == 1)
- {
- // Color-by-plane: RRR..., GGG..., BBB...
- $blob .= implode('', $temp[$count]);
- }
- else
- {
- // Color-by-pixel: RGB, RGB, RGB..
- }
- }
- else
- {
- $dimension = $cols*$rows;
-
- for ($i = 0; $i < $dimension; $i++)
- {
- $part = '';
- foreach ($temp[$count] as $segment)
- {
- $part = substr($segment, $i, 1) . $part;
- }
- $blob .= $part;
- }
- }
- }
- }
-
- // Blob here has "uncompressed" data (from raw or RLE)
- if ($photometric_interpretation == 'PALETTE COLOR')
- {
- $this->_samples_per_pixel = 3;
-
- $palettes = array();
- $palettes['R'] = array($this->get(0x0028,0x1101), $this->get(0x0028,0x1201));
- $palettes['G'] = array($this->get(0x0028,0x1102), $this->get(0x0028,0x1202));
- $palettes['B'] = array($this->get(0x0028,0x1103), $this->get(0x0028,0x1203));
- $palettes['A'] = array($this->get(0x0028,0x1104), $this->get(0x0028,0x1204));
- $entries = (int) $palettes['R'][0]['val1'];
- $entries = ($entries == 0) ? pow(2, 16) : $entries;
- $first = $palettes['R'][0]['val2'];
- $size = $palettes['R'][0]['val3'];
- $palette_byte_size = (int) $size/8;
- $offset = 8;
-
- $current_position = 0;
- $colors = array('R', 'G', 'B');
- // Now let's create the right values for the images
- for ($index = 0; $index < $number_of_frames; $index++)
- {
- // Create the image object
- $image = self::create_image($cols, $rows);
- for($y = 0; $y < $rows; $y++)
- {
- for ($x = 0; $x < $cols; $x++)
- {
- $rgb = array();
- $value = $this->_read_gray($blob, $current_position, $bytes_to_read);
- $palette_index = ($value <= $first)
- ? $first
- : (($value > $first + $entries) ? $first + $entries - 1 : $value);
- $current_position += $bytes_to_read;
-
- for ($sample = 0; $sample < 3; $sample++)
- {
-
- $tmp = $this->_read_gray($palettes[$colors[$sample]][1]
- , $palette_byte_size*$palette_index, $palette_byte_size);
-
- $rgb[$sample] = $tmp >> 8;
- }
-
- // Set the color
- $color = $this->_allocate_color_rgb($image, $rgb);
- $rgb = NULL;
- // Set the pixel value
- $this->_set_pixel($image, $x, $y, $color);
- $color = NULL;
- }
- }
-
- // Append the current image
- $images[] = $image;
- $image = NULL;
- }
-
- return $images;
- }
- else
- {
- // Do this if no window center and window width are set and when samples per pixel is 1 (gray images).
- if (($window_center == 0 OR $window_width == 0) AND $samples_per_pixel == 1)
- {
- $length = strlen($blob);
- // This is costly performance wise! :(
- while ($current_position < $starting_position + $length)
- {
- if ($current_position == $starting_position + $current_image*$image_size)
- {
- // A new image has been found
- $x = 0;
- $y = 0;
-
- $max[$current_image] = -200000; // Small enough so it will be properly calculated
- $min[$current_image] = 200000; // Large enough so it wil be properly calculated
- $current_image++;
- }
- $gray = $this->_read_gray($blob, $current_position, $bytes_to_read);
- $current_position += $bytes_to_read;
-
- // Getting the max
- if ($gray > $max[$current_image - 1])
- {
- // max
- $max[$current_image - 1] = $gray;
- }
- // Getting the min
- if ($gray < $min[$current_image - 1])
- {
- // min
- $min[$current_image - 1] = $gray;
- }
- $y++;
- if ($y == $cols)
- {
- // Next row
- $x++;
- $y = 0;
- }
- }
- }
- }
- $current_position = $starting_position = 0;
- // Now let's create the right values for the images
- for ($index = 0; $index < $number_of_frames; $index++)
- {
- if ($samples_per_pixel == 1)
- {
- // Real max and min according to window center & width (if set)
- $maximum = ($window_center != 0 AND $window_width != 0)
- ? round($window_center + $window_width/2)
- : $max[$index];
-
- $minimum = ($window_center != 0 AND $window_width != 0)
- ? round($window_center - $window_width/2)
- : $min[$index];
- // Check if window and level are sent
- $maximum = ( ! empty($window) AND ! empty($level))
- ? round($level + $window/2)
- : $maximum;
- $minimum = ( ! empty($window) AND ! empty($level))
- ? round($level - $window/2)
- : $minimum;
- if ($maximum == $minimum)
- {
- // Something wrong. Avoid having a zero division
- throw new Nanodicom_Exception('Division by zero', NULL, 302);
- }
- }
- // Create the image object
- $image = self::create_image($cols, $rows);
- $pixels = array();
- for($y = 0; $y < $rows; $y++)
- {
- for ($x = 0; $x < $cols; $x++)
- {
- switch ($samples_per_pixel)
- {
- case 1:
- $gray = $this->_read_gray($blob, $current_position, $bytes_to_read);
- $current_position += $bytes_to_read;
- // truncating pixel values over max and below min
- $gray = ($gray > $maximum) ? $maximum : $gray;
- $gray = ($gray < $minimum) ? $minimum : $gray;
- // Converting to gray value
- $gray = ($gray - $minimum)/($maximum - $minimum)*255;
- // For MONOCHROME1 we have to invert the pixel values.
- if ($photometric_interpretation == 'MONOCHROME1')
- {
- $gray = 255 - $gray;
- }
- // Set the (gray) color
- $color = $this->_allocate_color_gray($image, $gray);
- $gray = NULL;
- break;
- case 3:
- // It has 3 colors
- $rgb = array();
- for ($sample = 0; $sample < $samples_per_pixel; $sample++)
- {
- $current_position = ($planar_configuration == 0)
- ? $current_position
- : $sample * ($rows * $cols) + ($y*$cols + $x);
- $rgb[$sample] = $this->_read_gray($blob, $current_position, $bytes_to_read);
- $current_position += $bytes_to_read;
- }
-
- // Set the color
- $color = $this->_allocate_color_rgb($image, $rgb);
-
- $rgb = NULL;
- break;
- default:
- break;
- }
- // Set the pixel value
- $this->_set_pixel($image, $x, $y, $color);
- $color = NULL;
- }
- }
- // Append the current image
- $images[] = $image;
- $image = NULL;
- }
- // Collect the ending time for the profiler
- $this->profiler['pixel']['end'] = microtime(TRUE);
- return $images;
- }
- /**
- * Public method to write images based on library used and format
- *
- * @param resource the image object instance
- * @param string the format to be saved. defaults to jpeg
- * @return boolean true on success, false on failure
- */
- public function write_image($image, $location, $format = 'jpg')
- {
- switch (self::$driver)
- {
- case 'gd' :
- switch ($format)
- {
- case 'png': imagepng($image, $location.'.png');
- break;
- case 'gif': imagegif($image, $location.'.gif');
- break;
- case 'jpg':
- default:
- imagejpeg($image, $location.'.jpg');
- break;
- }
- break;
- case 'gmagick' :
- case 'imagick' :
- switch ($format)
- {
- case 'png': $format = 'png';
- break;
- case 'gif': $format = 'gif';
- break;
- default: $format = 'jpg';
- break;
- }
- $image->writeImage($location.'.'.$format);
- break;
- }
-
- return TRUE;
- }
- /**
- * Internal method to read image from blob (string)
- *
- * @param string the binary data to be read
- * @return mixed an instance of the image based on library (driver), false on failure
- */
- protected function _read_image_blob($string)
- {
- switch (self::$driver)
- {
- case 'gd' : return imagecreatefromstring($string);
- case 'gmagick' :
- case 'imagick' :
- $image = new imagick();
- $image->readImageBlob($string);
- return $image;
- }
-
- return FALSE;
- }
- /**
- * Internal method to allocate the color for image instance (color)
- *
- * @param resource the image object instance
- * @param integer the color value
- * @return mixed color value or class, false on failure
- */
- protected function _allocate_color_rgb($image, $rgb)
- {
- switch (self::$driver)
- {
- case 'gd': return imagecolorallocate($image, $rgb[0], $rgb[1], $rgb[2]);
- case 'gmagick': return new GmagickPixel('rgb('.$rgb[0].','.$rgb[1].','.$rgb[2].')');
- case 'imagick': return new ImagickPixel('rgb('.$rgb[0].','.$rgb[1].','.$rgb[2].')');
- }
-
- return FALSE;
- }
-
- /**
- * Internal method to allocate the color for image instance (gray)
- *
- * @param resource the image object instance
- * @param integer the gray value
- * @return mixed color value or class, false on failure
- */
- protected function _allocate_color_gray($image, $gray)
- {
- switch (self::$driver)
- {
- case 'gd': return imagecolorallocate($image, $gray, $gray, $gray);
- case 'gmagick': return new GmagickPixel('rgb('.$gray.','.$gray.','.$gray.')');
- case 'imagick': return new ImagickPixel('rgb('.$gray.','.$gray.','.$gray.')');
- }
-
- return FALSE;
- }
- /**
- * Internal method to set a pixel into the existing image object instance
- *
- * @param resource the image object instance
- * @param integer the x position
- * @param integer the y position
- * @param integer the color to be set
- * @return boolean true on success, false on failure
- */
- protected function _set_pixel( & $image, $x, $y, $color)
- {
- switch (self::$driver)
- {
- case 'gd': imagesetpixel($image, $x, $y, $color);
- return TRUE;
- case 'gmagick': $draw = new GmagickDraw();
- $draw->setFillColor($color);
- $draw->point($x, $y);
- $image->drawImage($draw);
- return TRUE;
- case 'imagick': $draw = new ImagickDraw();
- $draw->setFillColor($color);
- $draw->point($x, $y);
- $image->drawImage($draw);
- return TRUE;
- }
-
- return FALSE;
- }
- /**
- * Public static method to create an instance of the image
- *
- * @param integer the number of columns
- * @param integet the number of rows
- * @return mixed the corresponding object from the set driver or false on failure
- */
- public static function create_image($cols, $rows)
- {
- switch (self::$driver)
- {
- case 'gd': return imagecreatetruecolor($cols, $rows);
- case 'gmagick': $image = new Gmagick();
- $image->newImage($cols, $rows, 'none');
- return $image;
- case 'imagick': $image = new Imagick();
- $image->newImage($cols, $rows, 'none');
- return $image;
- }
-
- return FALSE;
- }
-
- /**
- * Internal method to check if drivers are installed
- *
- * @return bool true if driver is installed, false otherwise
- */
- protected function _check_driver()
- {
- switch (self::$driver)
- {
- case 'gd': return function_exists('imagecreatetruecolor');
- case 'gmagick': return class_exists('Gmagick');
- //case 'imagick': return (class_exists('Imagick') AND method_exists('Imagick', 'importImagePixels'));
- case 'imagick': return class_exists('Imagick');
- }
-
- return FALSE;
- }
-
- /**
- * Internal method to read a 'gray' value.
- *
- * @param string the blob that holds the pixel data
- * @param integer the current position in the string to read
- * @param integet the number of bytes to read
- * @return integer the gray or color value at the given location
- */
- protected function _read_gray($blob, $current_position, $bytes_to_read)
- {
- if ($this->_samples_per_pixel == 1)
- {
- $chunk = substr($blob, $current_position, $bytes_to_read);
- $gray = $this->{Nanodicom::$_read_int}($bytes_to_read, $this->_endian, $bytes_to_read, Nanodicom::UNSIGNED, $chunk);
- $chunk = NULL;
-
- // Checking if 2's complement
- $gray = ($this->_pixel_representation)
- ? self::complement2($gray, $this->_high_bit)
- : $gray;
- // Getting the right value according to slope and intercept
- $gray = $gray*$this->_rescale_slope + $this->_rescale_intercept;
- // Multiplying for dose_grid_scaling
- return $gray*$this->_dose_scaling;
- }
- else
- {
- // Read current position, it is 3 samples
- $chunk = substr($blob, $current_position, $bytes_to_read);
- return $this->{Nanodicom::$_read_int}($bytes_to_read, $this->_endian, $bytes_to_read, Nanodicom::UNSIGNED, $chunk);
- }
- }
- /**
- * Static method to find the complement of 2, returns an integer.
- *
- * @param integer the integer number to convert
- * @param integer the high bit for the value. By default 15 (assumes 2 bytes)
- * @return integer the number after complement's 2 applied
- */
- public static function complement2($number, $high_bit = 15)
- {
- $sign = $number >> $high_bit;
- if ($sign)
- {
- // Negative
- $number = -pow(2, $high_bit + 1) - $number;
- }
- return $number;
- }
- }
|