github.com/jjjabc/fitsio@v0.0.0-20161215022839-d1807e9e818e/image.go (about)

     1  // Copyright 2015 The astrogo Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package fitsio
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"image"
    11  	"reflect"
    12  
    13  	"github.com/astrogo/fitsio/fltimg"
    14  	"github.com/gonuts/binary"
    15  )
    16  
    17  // Image represents a FITS image
    18  type Image interface {
    19  	HDU
    20  	Read(ptr interface{}) error
    21  	Write(ptr interface{}) error
    22  	Raw() []byte
    23  	Image() image.Image
    24  
    25  	freeze() error
    26  }
    27  
    28  // imageHDU is a Header-Data Unit extension holding an image as data payload
    29  type imageHDU struct {
    30  	hdr Header
    31  	raw []byte
    32  }
    33  
    34  // NewImage creates a new Image with bitpix size for the pixels and axes as its axes
    35  func NewImage(bitpix int, axes []int) *imageHDU {
    36  	hdr := NewHeader(nil, IMAGE_HDU, bitpix, axes)
    37  	return &imageHDU{
    38  		hdr: *hdr,
    39  		raw: make([]byte, 0),
    40  	}
    41  }
    42  
    43  // Close closes this HDU, cleaning up cycles (if any) for garbage collection
    44  func (img *imageHDU) Close() error {
    45  	return nil
    46  }
    47  
    48  // Header returns the Header part of this HDU block.
    49  func (img *imageHDU) Header() *Header {
    50  	return &img.hdr
    51  }
    52  
    53  // Type returns the Type of this HDU
    54  func (img *imageHDU) Type() HDUType {
    55  	return img.hdr.Type()
    56  }
    57  
    58  // Name returns the value of the 'EXTNAME' Card.
    59  func (img *imageHDU) Name() string {
    60  	card := img.hdr.Get("EXTNAME")
    61  	if card == nil {
    62  		return ""
    63  	}
    64  	return card.Value.(string)
    65  }
    66  
    67  // Version returns the value of the 'EXTVER' Card (or 1 if none)
    68  func (img *imageHDU) Version() int {
    69  	card := img.hdr.Get("EXTVER")
    70  	if card == nil {
    71  		return 1
    72  	}
    73  	return card.Value.(int)
    74  }
    75  
    76  // Raw returns the raw bytes which make the image
    77  func (img *imageHDU) Raw() []byte {
    78  	return img.raw
    79  }
    80  
    81  // Read reads the image data into ptr
    82  func (img *imageHDU) Read(ptr interface{}) error {
    83  	var err error
    84  	if img.raw == nil {
    85  		// FIXME(sbinet): load data from file
    86  		panic(fmt.Errorf("image with no raw data"))
    87  	}
    88  
    89  	hdr := img.Header()
    90  	nelmts := 1
    91  	for _, dim := range hdr.Axes() {
    92  		nelmts *= dim
    93  	}
    94  
    95  	if len(hdr.Axes()) <= 0 {
    96  		nelmts = 0
    97  	}
    98  
    99  	//rv := reflect.Indirect(reflect.ValueOf(ptr))
   100  	rv := reflect.ValueOf(ptr).Elem()
   101  	rt := rv.Type()
   102  
   103  	if rt.Kind() != reflect.Slice && rt.Kind() != reflect.Array {
   104  		return fmt.Errorf("fitsio: invalid type (%v). expected array or slice", rt.Kind())
   105  	}
   106  
   107  	pixsz := hdr.Bitpix() / 8
   108  	if pixsz < 0 {
   109  		pixsz = -pixsz
   110  	}
   111  	otype := rt.Elem()
   112  	if int(otype.Size()) != pixsz {
   113  		return fmt.Errorf(
   114  			"fitsio: element-size do not match. bitpix=%d. elmt-size=%d (conversion not yet supported)",
   115  			hdr.Bitpix(), otype.Size())
   116  	}
   117  
   118  	if rt.Kind() == reflect.Slice {
   119  		rv.SetLen(nelmts)
   120  	}
   121  
   122  	var cnv = func(v reflect.Value) reflect.Value {
   123  		return v
   124  	}
   125  
   126  	bdec := binary.NewDecoder(bytes.NewBuffer(img.raw))
   127  	bdec.Order = binary.BigEndian
   128  
   129  	switch hdr.Bitpix() {
   130  	case 8:
   131  		itype := reflect.TypeOf((*byte)(nil)).Elem()
   132  		if !rt.Elem().ConvertibleTo(itype) {
   133  			return fmt.Errorf("fitsio: can not convert []byte to %s", rt.Name())
   134  		}
   135  		if itype != otype {
   136  			cnv = func(v reflect.Value) reflect.Value {
   137  				return v.Convert(otype)
   138  			}
   139  		}
   140  		for i := 0; i < nelmts; i++ {
   141  			var v byte
   142  			err = bdec.Decode(&v)
   143  			if err != nil {
   144  				return fmt.Errorf("fitsio: %v", err)
   145  			}
   146  			rv.Index(i).Set(cnv(reflect.ValueOf(v)))
   147  		}
   148  
   149  	case 16:
   150  		itype := reflect.TypeOf((*int16)(nil)).Elem()
   151  		if !rt.Elem().ConvertibleTo(itype) {
   152  			return fmt.Errorf("fitsio: can not convert []int16 to %s", rt.Name())
   153  		}
   154  		if itype != otype {
   155  			cnv = func(v reflect.Value) reflect.Value {
   156  				return v.Convert(otype)
   157  			}
   158  		}
   159  		for i := 0; i < nelmts; i++ {
   160  			var v int16
   161  			err = bdec.Decode(&v)
   162  			if err != nil {
   163  				return fmt.Errorf("fitsio: %v", err)
   164  			}
   165  			rv.Index(i).Set(cnv(reflect.ValueOf(v)))
   166  		}
   167  
   168  	case 32:
   169  		itype := reflect.TypeOf((*int32)(nil)).Elem()
   170  		if !rt.Elem().ConvertibleTo(itype) {
   171  			return fmt.Errorf("fitsio: can not convert []int32 to %s", rt.Name())
   172  		}
   173  		if itype != otype {
   174  			cnv = func(v reflect.Value) reflect.Value {
   175  				return v.Convert(otype)
   176  			}
   177  		}
   178  		for i := 0; i < nelmts; i++ {
   179  			var v int32
   180  			err = bdec.Decode(&v)
   181  			if err != nil {
   182  				return fmt.Errorf("fitsio: %v", err)
   183  			}
   184  			rv.Index(i).Set(cnv(reflect.ValueOf(v)))
   185  		}
   186  
   187  	case 64:
   188  		itype := reflect.TypeOf((*int64)(nil)).Elem()
   189  		if !rt.Elem().ConvertibleTo(itype) {
   190  			return fmt.Errorf("fitsio: can not convert []int64 to %s", rt.Name())
   191  		}
   192  		if itype != otype {
   193  			cnv = func(v reflect.Value) reflect.Value {
   194  				return v.Convert(otype)
   195  			}
   196  		}
   197  		for i := 0; i < nelmts; i++ {
   198  			var v int64
   199  			err = bdec.Decode(&v)
   200  			if err != nil {
   201  				return fmt.Errorf("fitsio: %v", err)
   202  			}
   203  			rv.Index(i).Set(cnv(reflect.ValueOf(v)))
   204  		}
   205  
   206  	case -32:
   207  		itype := reflect.TypeOf((*float32)(nil)).Elem()
   208  		if !rt.Elem().ConvertibleTo(itype) {
   209  			return fmt.Errorf("fitsio: can not convert []float32 to %s", rt.Name())
   210  		}
   211  		if itype != otype {
   212  			cnv = func(v reflect.Value) reflect.Value {
   213  				return v.Convert(otype)
   214  			}
   215  		}
   216  		for i := 0; i < nelmts; i++ {
   217  			var v float32
   218  			err = bdec.Decode(&v)
   219  			if err != nil {
   220  				return fmt.Errorf("fitsio: %v", err)
   221  			}
   222  			rv.Index(i).Set(cnv(reflect.ValueOf(v)))
   223  		}
   224  
   225  	case -64:
   226  		itype := reflect.TypeOf((*byte)(nil)).Elem()
   227  		if !rt.Elem().ConvertibleTo(itype) {
   228  			return fmt.Errorf("fitsio: can not convert []float64 to %s", rt.Name())
   229  		}
   230  		if itype != otype {
   231  			cnv = func(v reflect.Value) reflect.Value {
   232  				return v.Convert(otype)
   233  			}
   234  		}
   235  		for i := 0; i < nelmts; i++ {
   236  			var v float64
   237  			err = bdec.Decode(&v)
   238  			if err != nil {
   239  				return fmt.Errorf("fitsio: %v", err)
   240  			}
   241  			rv.Index(i).Set(cnv(reflect.ValueOf(v)))
   242  		}
   243  
   244  	default:
   245  		return fmt.Errorf("invalid image type [bitpix=%d]", hdr.Bitpix())
   246  	}
   247  
   248  	return err
   249  }
   250  
   251  // Write writes the given image data to the HDU
   252  func (img *imageHDU) Write(data interface{}) error {
   253  	var err error
   254  	rv := reflect.ValueOf(data).Elem()
   255  	if !rv.CanAddr() {
   256  		return fmt.Errorf("fitsio: %T is not addressable", data)
   257  	}
   258  
   259  	hdr := img.Header()
   260  	naxes := len(hdr.Axes())
   261  	if naxes == 0 {
   262  		return nil
   263  	}
   264  	nelmts := 1
   265  	for _, dim := range hdr.Axes() {
   266  		nelmts *= dim
   267  	}
   268  
   269  	pixsz := hdr.Bitpix() / 8
   270  	if pixsz < 0 {
   271  		pixsz = -pixsz
   272  	}
   273  
   274  	img.raw = make([]byte, pixsz*nelmts)
   275  	buf := &sectionWriter{
   276  		buf: img.raw,
   277  		beg: 0,
   278  	}
   279  	enc := binary.NewEncoder(buf)
   280  	enc.Order = binary.BigEndian
   281  
   282  	switch data := rv.Interface().(type) {
   283  	case []byte:
   284  		if hdr.Bitpix() != 8 {
   285  			return fmt.Errorf("fitsio: got a %T but bitpix!=%d", data, hdr.Bitpix())
   286  		}
   287  		for _, v := range data {
   288  			err = enc.Encode(&v)
   289  			if err != nil {
   290  				return fmt.Errorf("fitsio: %v", err)
   291  			}
   292  		}
   293  
   294  	case []int8:
   295  		if hdr.Bitpix() != 8 {
   296  			return fmt.Errorf("fitsio: got a %T but bitpix!=%d", data, hdr.Bitpix())
   297  		}
   298  		for _, v := range data {
   299  			err = enc.Encode(&v)
   300  			if err != nil {
   301  				return fmt.Errorf("fitsio: %v", err)
   302  			}
   303  		}
   304  
   305  	case []int16:
   306  		if hdr.Bitpix() != 16 {
   307  			return fmt.Errorf("fitsio: got a %T but bitpix!=%d", data, hdr.Bitpix())
   308  		}
   309  		for _, v := range data {
   310  			err = enc.Encode(&v)
   311  			if err != nil {
   312  				return fmt.Errorf("fitsio: %v", err)
   313  			}
   314  		}
   315  
   316  	case []uint16:
   317  		if hdr.Bitpix() != 16 {
   318  			return fmt.Errorf("fitsio: got a %T but bitpix!=%d", data, hdr.Bitpix())
   319  		}
   320  		for _, v := range data {
   321  			err = enc.Encode(&v)
   322  			if err != nil {
   323  				return fmt.Errorf("fitsio: %v", err)
   324  			}
   325  		}
   326  
   327  	case []int32:
   328  		if hdr.Bitpix() != 32 {
   329  			return fmt.Errorf("fitsio: got a %T but bitpix!=%d", data, hdr.Bitpix())
   330  		}
   331  		for _, v := range data {
   332  			err = enc.Encode(&v)
   333  			if err != nil {
   334  				return fmt.Errorf("fitsio: %v", err)
   335  			}
   336  		}
   337  
   338  	case []int64:
   339  		if hdr.Bitpix() != 64 {
   340  			return fmt.Errorf("fitsio: got a %T but bitpix!=%d", data, hdr.Bitpix())
   341  		}
   342  		for _, v := range data {
   343  			err = enc.Encode(&v)
   344  			if err != nil {
   345  				return fmt.Errorf("fitsio: %v", err)
   346  			}
   347  		}
   348  
   349  	case []float32:
   350  		if hdr.Bitpix() != -32 {
   351  			return fmt.Errorf("fitsio: got a %T but bitpix!=%d", data, hdr.Bitpix())
   352  		}
   353  		for _, v := range data {
   354  			err = enc.Encode(&v)
   355  			if err != nil {
   356  				return fmt.Errorf("fitsio: %v", err)
   357  			}
   358  		}
   359  
   360  	case []float64:
   361  		if hdr.Bitpix() != -64 {
   362  			return fmt.Errorf("fitsio: got a %T but bitpix!=%d", data, hdr.Bitpix())
   363  		}
   364  		for _, v := range data {
   365  			err = enc.Encode(&v)
   366  			if err != nil {
   367  				return fmt.Errorf("fitsio: %v", err)
   368  			}
   369  		}
   370  
   371  	default:
   372  		return fmt.Errorf("fitsio: invalid image type (%T)", rv.Interface())
   373  	}
   374  
   375  	//img.raw = buf.Bytes()
   376  	return err
   377  }
   378  
   379  // Image returns an image.Image value.
   380  func (img *imageHDU) Image() image.Image {
   381  
   382  	// Getting the HDU bitpix and axes.
   383  	header := img.Header()
   384  	bitpix := header.Bitpix()
   385  	axes := header.Axes()
   386  	raw := img.Raw()
   387  
   388  	if len(axes) < 2 {
   389  		return nil
   390  	}
   391  
   392  	// Image width and height.
   393  	w := axes[0]
   394  	h := axes[1]
   395  
   396  	switch {
   397  	case w <= 0:
   398  		return nil
   399  	case h <= 0:
   400  		return nil
   401  	}
   402  
   403  	rect := image.Rect(0, 0, w, h)
   404  
   405  	switch bitpix {
   406  	case 8:
   407  		img := &image.Gray{
   408  			Pix:    raw,
   409  			Stride: 1 * w,
   410  			Rect:   rect,
   411  		}
   412  		return img
   413  
   414  	case 16:
   415  		img := &image.Gray16{
   416  			Pix:    raw,
   417  			Stride: 2 * w,
   418  			Rect:   rect,
   419  		}
   420  		return img
   421  
   422  	case 32:
   423  		img := &image.RGBA{
   424  			Pix:    raw,
   425  			Stride: 4 * w,
   426  			Rect:   rect,
   427  		}
   428  		return img
   429  
   430  	case 64:
   431  		img := &image.RGBA64{
   432  			Pix:    raw,
   433  			Stride: 8 * w,
   434  			Rect:   rect,
   435  		}
   436  		return img
   437  
   438  	case -32:
   439  		img := fltimg.NewGray32(rect, raw)
   440  		return img
   441  
   442  	case -64:
   443  		img := fltimg.NewGray64(rect, raw)
   444  		return img
   445  
   446  	default:
   447  		panic(fmt.Errorf("fitsio: image with unknown BITPIX value of %d", bitpix))
   448  	}
   449  
   450  }
   451  
   452  // freeze freezes an Image before writing, finalizing header values.
   453  func (img *imageHDU) freeze() error {
   454  	var err error
   455  	card := img.Header().Get("XTENSION")
   456  	if card != nil {
   457  		return err
   458  	}
   459  
   460  	err = img.Header().prepend(Card{
   461  		Name:    "XTENSION",
   462  		Value:   "IMAGE   ",
   463  		Comment: "IMAGE extension",
   464  	})
   465  	if err != nil {
   466  		return err
   467  	}
   468  
   469  	return err
   470  }