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 := §ionWriter{ 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 }