def read_image(pixel_data, columns, rows, options={})
      raise ArgumentError, "Expected Array for pixel_data, got #{pixel_data.class}" unless pixel_data.is_a?(Array)
      raise ArgumentError, "Expected Integer for columns, got #{columns.class}" unless columns.is_a?(Integer)
      raise ArgumentError, "Expected Rows for columns, got #{rows.class}" unless rows.is_a?(Integer)
      raise ArgumentError, "Size of pixel_data must be at least equal to columns*rows. Got #{columns}*#{rows}=#{columns*rows}, which is less than the array size #{pixel_data.length}" if columns * rows > pixel_data.length
      # Remap the image from pixel values to presentation values if the user has requested this:
      if options[:remap] or options[:level]
        # How to perform the remapping? NArray (fast) or Ruby Array (slow)?
        if options[:narray] == true
          pixel_data = process_presentation_values_narray(pixel_data, 0, 65535, options[:level]).to_a
        else
          pixel_data = process_presentation_values(pixel_data, 0, 65535, options[:level])
        end
      else
        # No remapping, but make sure that we pass on unsigned pixel values to the image processor:
        pixel_data = pixel_data.to_unsigned(bit_depth) if signed_pixels?
      end
      image = import_pixels(pixel_data.to_blob(actual_bit_depth), columns, rows, actual_bit_depth, photometry)
      return image
    end
def read_image(pixel_data, columns, rows, options={})
      raise ArgumentError, "Expected Array for pixel_data, got #{pixel_data.class}" unless pixel_data.is_a?(Array)
      raise ArgumentError, "Expected Integer for columns, got #{columns.class}" unless columns.is_a?(Integer)
      raise ArgumentError, "Expected Rows for columns, got #{rows.class}" unless rows.is_a?(Integer)
      raise ArgumentError, "Size of pixel_data must be at least equal to columns*rows. Got #{columns}*#{rows}=#{columns*rows}, which is less than the array size #{pixel_data.length}" if columns * rows > pixel_data.length
      # Remap the image from pixel values to presentation values if the user has requested this:
      if options[:remap] or options[:level]
        # How to perform the remapping? NArray (fast) or Ruby Array (slow)?
        if options[:narray] == true
          pixel_data = process_presentation_values_narray(pixel_data, 0, 65535, options[:level]).to_a
        else
          pixel_data = process_presentation_values(pixel_data, 0, 65535, options[:level])
        end
      else
        # No remapping, but make sure that we pass on unsigned pixel values to the image processor:
        pixel_data = pixel_data.to_unsigned(bit_depth) if signed_pixels?
      end
      image = import_pixels(pixel_data.to_blob(actual_bit_depth), columns, rows, actual_bit_depth, photometry)
      return image
    end
Creates an image object from the specified pixel value array, performing
 presentation value processing if requested.

 @note Definitions for Window Center and Width can be found in the DICOM standard, PS 3.3 C.11.2.1.2

 @param [Array<Integer>] pixel_data an array of pixel values
 @param [Integer] columns the number of columns in the pixel data
 @param [Integer] rows the number of rows in the pixel data
 @param [Hash] options the options to use for reading the image
 @option options [Boolean] :remap if true, pixel values are remapped to presentation values (using intercept and slope values from the DICOM object)
 @option options [Boolean, Array<Integer>] :level if true, window leveling is performed using default values from the DICOM object, or if an array ([center, width]) is specified, these custom values are used instead
 @option options [Boolean] :narray if true, forces the use of NArray for the pixel remap process (for faster execution)
 @return [MagickImage] the extracted image object