Class: RFits::ImageData

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/rfits/rfits.rb

Overview

Represents the actual pixels in a FITS file.

Constant Summary collapse

BITPIX_IMG_MAP =
{
  IO::Proxy::BYTE_IMG => IO::Proxy::TBYTE,
  IO::Proxy::SHORT_IMG => IO::Proxy::TSHORT,
  IO::Proxy::LONG_IMG => IO::Proxy::TLONG,
  IO::Proxy::LONGLONG_IMG => IO::Proxy::TLONGLONG,
  IO::Proxy::FLOAT_IMG => IO::Proxy::TFLOAT,
  IO::Proxy::DOUBLE_IMG => IO::Proxy::TDOUBLE,
  IO::Proxy::SBYTE_IMG => IO::Proxy::TSBYTE,
  IO::Proxy::USHORT_IMG => IO::Proxy::TUSHORT,
  IO::Proxy::ULONG_IMG => IO::Proxy::TULONG
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(image) ⇒ ImageData

Create an ImageData object associated with the specified image.



392
393
394
# File 'lib/rfits/rfits.rb', line 392

def initialize(image)
  @image = image
end

Instance Attribute Details

#imageObject (readonly)

The image the data belongs to.



389
390
391
# File 'lib/rfits/rfits.rb', line 389

def image
  @image
end

Instance Method Details

#[](*args) ⇒ Object

An alias for ImageData#pixels, but automatically chooses the datatype for you based on the type of image (i.e. BITPIX).



398
399
400
# File 'lib/rfits/rfits.rb', line 398

def [](*args)
  self.pixels(BITPIX_IMG_MAP[self.image.bitpix], *args)
end

#[]=(*args) ⇒ Object

An alias for ImageData#set_pixels, but automatically chooses the datatype for you based on the type of image (i.e. BITPIX).



404
405
406
# File 'lib/rfits/rfits.rb', line 404

def []=(*args)
  self.set_pixels(BITPIX_IMG_MAP[self.image.bitpix], *args)
end

#column(i) ⇒ Object

Retrieve the ith (where i is zero-based) column of the image array. Assumes a two-dimensional image.



570
571
572
573
574
575
576
577
# File 'lib/rfits/rfits.rb', line 570

def column(i)
  pixel_list = []
  (0...self.image.naxes.first).each do |row|
    pixel_list << self[i, row]
  end
  
  pixel_list
end

#eachObject

Iterate through each pixel in the array.



543
544
545
546
547
# File 'lib/rfits/rfits.rb', line 543

def each
  (0...self.length).each do |i|
    yield self[i]
  end
end

#each_columnObject

Iterate through each column of pixels in the array. Assumes a two-dimensional image.



588
589
590
591
592
# File 'lib/rfits/rfits.rb', line 588

def each_column
  (0...self.image.naxes.last).each do |i|
    yield(self.column(i), i)
  end
end

#each_rowObject

Iterate through each row of pixels in the array. Assumes a two-dimensional image.



562
563
564
565
566
# File 'lib/rfits/rfits.rb', line 562

def each_row
  (0...self.image.naxes.last).each do |i|
    yield(self.row(i), i)
  end
end

#firstObject

The first pixel in the image array.



595
596
597
# File 'lib/rfits/rfits.rb', line 595

def first
  self[0]
end

#lastObject

The last pixel in the image array.



600
601
602
# File 'lib/rfits/rfits.rb', line 600

def last
  self[self.length - 1]
end

#lengthObject

The number of pixels in the image array.



538
539
540
# File 'lib/rfits/rfits.rb', line 538

def length
  self.image.naxes.inject(1){ |product, n| product *= n }
end

#pixels(datatype, *args) ⇒ Object

Retrieve the value of a pixel, or a range of pixels. The value is coerced into an appropriate class according to the datatype provided. Can be used in a number of ways. The simplest plucks the nth pixel from the image array:

image.data.pixels(IO::Proxy::TSHORT, 0)  # first pixel in the image (remember zero-based indexing?)
image.data.pixels(IO::Proxy::TSHORT, image.data[image.data.size - 1])  # last pixel in the image

You can also choose a range of contiguous pixels, in which case an array of values is returned:

image.data.pixels(IO::Proxy::TSHORT, 0..10)  # retrieve first 10 pixels

or pick the single pixel at a particular coordinate:

image.data.pixels(IO::Proxy::TSHORT, 4, 5)

or equivalently:

image.data.pixels(IO::Proxy::TSHORT, [4, 5])

In addition, you can give an (x,y) coordinate and a number of pixels, assuming the coordinates are zero-based:

image.data.pixels(IO::Proxy::TSHORT, [4, 5], 10)  # 10 pixels starting at the point [4, 5]

or get all the pixels between two points:

image.data.pixels(IO::Proxy::TSHORT, [4, 5], [6, 6]) # all the pixels between the points [4, 5] and [6, 6]


434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
# File 'lib/rfits/rfits.rb', line 434

def pixels(datatype, *args)
  self.image.reset_position()
  
  pixnum = nelem = nil
  pixel_values = case args.size
    when 1  # 1 argument
      if args.first.is_a?(Range) # a range of pixels
        pixnum = args.first.first.to_i < 0 ? self.length + args.first.first.to_i : args.first.first.to_i + 1
        nelem = args.first.exclude_end? ?
          args.first.last.to_i - args.first.first.to_i + 1:
          args.first.last.to_i - args.first.first.to_i
      elsif args.first.is_a?(Fixnum) # the nth pixel
        pixnum = args.first.to_i < 0 ? self.length + args.first.to_i : args.first.to_i + 1
        nelem = 1
      elsif args.first.is_a?(Array) # array representing single pixel
        return(IO::Proxy.fits_read_pix(self.image.file.io, datatype, args.first.collect{ |c| c + 1 }, 1, nil).first.first)
      else
        raise ArgumentError, "Argument should be an integer, an integer range, or an array of integers."
      end

      IO::Proxy.fits_read_img(self.image.file.io, datatype, pixnum, nelem, nil).first
    when 2 # 2 arguments
      if args.first.is_a?(Fixnum) and args.last.is_a?(Fixnum) # the pixel at the coordinate
        IO::Proxy.fits_read_pix(self.image.file.io, datatype, [args.first.to_i+1, args.last.to_i+1], 1, nil).first
      elsif args.first.is_a?(Array) and args.last.is_a?(Fixnum) # a starting coordinate + some number of pixels
        fpixel = args.first.collect{ |p| p + 1 }
        IO::Proxy.fits_read_pix(self.image.file.io, datatype, fpixel, args.last.to_i, nil).first
      elsif args.first.is_a?(Array) and args.last.is_a?(Array) # a starting coordinate + ending coordinate
        fpixel = args.first.collect{ |p| p + 1 }
        lpixel = args.last.collect{ |p| p + 1 }
        IO::Proxy.fits_read_subset(self.image.file.io, datatype, fpixel, lpixel, args.first.collect{ |dim| 1 }, nil).first
      else
        raise ArgumentError, "Arguments should be two fixnums, an array and a fixnum, or two arrays."
      end
    else
      raise ArgumentError, "Arguments should be an integer, an integer range, an array of integers, two fixnums, an array and a fixnum, or two arrays."
  end

  pixel_values.size == 1 ? pixel_values.first : pixel_values
end

#row(i) ⇒ Object

Retrieve the ith (where i is zero-based) row of the image array. Assumes a two-dimensional image.



551
552
553
# File 'lib/rfits/rfits.rb', line 551

def row(i)
  self[[0, i], self.image.naxes.first]
end

#set_column(i, vals) ⇒ Object

Set the ith column of a 2D image to the specified pixels.



580
581
582
583
584
# File 'lib/rfits/rfits.rb', line 580

def set_column(i, vals)
  (0...self.image.naxes.first).each do |row|
    self[i, row] = vals[row]
  end
end

#set_pixels(datatype, *args) ⇒ Object

Set the value of a pixel, or a range of pixels. The value is coerced into an appropriate class according to the datatype provided. Can be used in a number of ways. The simplest sets the nth pixel in the image array:

image.data.set_pixels(IO::Proxy::TSHORT, 0, 5)  # first pixel in the image is set to 5

You can also set a range of contiguous pixels:

image.data.set_pixels(IO::Proxy::TSHORT, 0..10, [5, 5, 3, 1, 0, 9, 5, 6, 7, 1])  # set first 10 pixels

or set the single pixel at a particular coordinate:

image.data.set_pixels(IO::Proxy::TSHORT, 4, 5, 10) # set pixel at [4, 5] to 10

or equivalently:

image.data.set_pixels(IO::Proxy::TSHORT, [4, 5], 10)

In addition, you can give an (x,y) coordinate and a list of pixels, assuming the coordinates are zero-based:

image.data.set_pixels(IO::Proxy::TSHORT, [4, 5], [1, 2, 3, 4])  # four pixels with values 1, 2, 3, 4 starting at [4, 5]

or set the pixels between two points:

image.data.set_pixels(IO::Proxy::TSHORT, [0, 0], [1, 1], [5, 6, 7, 8]) # set pixels to (5, 6, 7, 8) between [0, 0] and [1, 1]


500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
# File 'lib/rfits/rfits.rb', line 500

def set_pixels(datatype, *args)
  self.image.reset_position()
  
  val = args.last.is_a?(Array) ? args.last : [args.last]
  case args.size
    when 2
      if args.first.is_a?(Fixnum)
        pixnum = args.first.to_i < 0 ? self.length + args.first.to_i : args.first.to_i + 1
        IO::Proxy.fits_write_img(self.image.file.io, datatype, pixnum, val.size, val)
      elsif args.first.is_a?(Range)
        pixnum = args.first.first.to_i < 0 ? self.length + args.first.first.to_i : args.first.first.to_i + 1
        nelem = args.first.exclude_end? ?
          args.first.last.to_i - args.first.first.to_i + 1:
          args.first.last.to_i - args.first.first.to_i
        IO::Proxy.fits_write_img(self.image.file.io, datatype, pixnum, nelem, val)
      elsif(args.first.is_a?(Array))
        IO::Proxy.fits_write_pixnull(self.image.file.io, datatype, args.first.collect{ |c| c + 1 }, val.size, val, nil)
      else
        raise ArgumentError, "Selector should be an integer, an integer range, or an array of integers."
      end
    when 3
      if args.first.is_a?(Fixnum) and args[-2].is_a?(Fixnum)
        IO::Proxy.fits_write_pixnull(self.image.file.io, datatype, args[0..-2].collect{ |c| c + 1 }, val.size, val, nil)
      elsif args.first.is_a?(Array) and args[-2].is_a?(Fixnum)
        IO::Proxy.fits_write_pixnull(self.image.file.io, datatype, args.first.collect{ |c| c + 1 }, args[-2], val, nil)
      elsif args.first.is_a?(Array) and args[-2].is_a?(Array)
        IO::Proxy.fits_write_subset(self.image.file.io, datatype,
          args.first.collect{ |c| c + 1 }, args[1].collect{ |c| c + 1 },
          val)
      else
        raise ArgumentError, "Selectors should be two fixnums, an array and a fixnum, or two arrays."
      end
    else
      raise ArgumentError, "Selectors should be an integer, an integer range, an array of integers, two fixnums, an array and a fixnum, or two arrays."
  end
end

#set_row(i, vals) ⇒ Object

Set the ith row of a 2D image to the specified pixels.



556
557
558
# File 'lib/rfits/rfits.rb', line 556

def set_row(i, vals)
  self[[0, i], self.image.naxes.first] = vals
end