github.com/astrogo/fitsio@v0.3.0/fltimg/image.go (about)

     1  // Copyright 2016 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  // fltimg provides image.Image implementations for the float32- and float64-image encodings of FITS.
     6  package fltimg
     7  
     8  import (
     9  	"encoding/binary"
    10  	"image"
    11  	"image/color"
    12  	"math"
    13  )
    14  
    15  const (
    16  	gamma = 1 / 2.2
    17  )
    18  
    19  type f32Gray float32
    20  
    21  func (c f32Gray) RGBA() (r, g, b, a uint32) {
    22  	f := math.Pow(float64(c), gamma)
    23  	switch {
    24  	case f > 1:
    25  		f = 1
    26  	case f < 0:
    27  		f = 0
    28  	}
    29  	i := uint32(f * 0xffff)
    30  	return i, i, i, 0xffff
    31  }
    32  
    33  // Gray32 represents an image.Image encoded in 32b IEEE floating-point values
    34  type Gray32 struct {
    35  	Pix    []uint8
    36  	Stride int
    37  	Rect   image.Rectangle
    38  	Min    float32
    39  	Max    float32
    40  }
    41  
    42  // NewGray32 creates a new Gray32 image with the given bounds.
    43  func NewGray32(rect image.Rectangle, pix []byte) *Gray32 {
    44  	w, h := rect.Dx(), rect.Dy()
    45  	switch {
    46  	case pix == nil:
    47  		panic("fltimg: nil pixel buffer")
    48  	case len(pix) != 4*w*h:
    49  		panic("fltimg: inconsistent pixels size")
    50  	}
    51  	img := &Gray32{Pix: pix, Stride: 4 * w, Rect: rect}
    52  	min := float32(+math.MaxFloat32)
    53  	max := float32(-math.MaxFloat32)
    54  	for i := 0; i < len(img.Pix); i += 4 {
    55  		v := img.at(i)
    56  		if v > max {
    57  			max = v
    58  		}
    59  		if v < min {
    60  			min = v
    61  		}
    62  	}
    63  	img.Min = min
    64  	img.Max = max
    65  	return img
    66  }
    67  
    68  func (p *Gray32) at(i int) float32 {
    69  	bits := binary.BigEndian.Uint32(p.Pix[i : i+4])
    70  	return math.Float32frombits(bits)
    71  }
    72  
    73  func (p *Gray32) setf(i int, v float32) {
    74  	binary.BigEndian.PutUint32(p.Pix[i:i+4], math.Float32bits(v))
    75  }
    76  
    77  func (p *Gray32) ColorModel() color.Model { return Gray32Model }
    78  func (p *Gray32) Bounds() image.Rectangle { return p.Rect }
    79  func (p *Gray32) At(x, y int) color.Color {
    80  	i := p.PixOffset(x, y)
    81  	v := p.at(i)
    82  	f := (v - p.Min) / (p.Max - p.Min)
    83  	switch {
    84  	case f < 0:
    85  		f = 0
    86  	case f > 1:
    87  		f = 1
    88  	}
    89  	return f32Gray(f)
    90  }
    91  
    92  func (p *Gray32) Set(x, y int, c color.Color) {
    93  	i := p.PixOffset(x, y)
    94  	r, _, _, _ := Gray32Model.Convert(c).RGBA()
    95  	v := math.Exp(math.Log(float64(r)/float64(0xffff)) / gamma)
    96  	p.setf(i, float32(v))
    97  }
    98  
    99  func (p *Gray32) PixOffset(x, y int) int {
   100  	return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4
   101  }
   102  
   103  type f64Gray float64
   104  
   105  func (c f64Gray) RGBA() (r, g, b, a uint32) {
   106  	f := math.Pow(float64(c), gamma)
   107  	switch {
   108  	case f > 1:
   109  		f = 1
   110  	case f < 0:
   111  		f = 0
   112  	}
   113  	i := uint32(f * 0xffff)
   114  	return i, i, i, 0xffff
   115  }
   116  
   117  // Gray64 represents an image.Image encoded in 64b IEEE floating-point values
   118  type Gray64 struct {
   119  	Pix    []uint8
   120  	Stride int
   121  	Rect   image.Rectangle
   122  	Min    float64
   123  	Max    float64
   124  }
   125  
   126  // NewGray64 creates a new Gray64 image with the given bounds.
   127  func NewGray64(rect image.Rectangle, pix []byte) *Gray64 {
   128  	w, h := rect.Dx(), rect.Dy()
   129  	switch {
   130  	case pix == nil:
   131  		panic("fltimg: nil pixel buffer")
   132  	case len(pix) != 8*w*h:
   133  		panic("fltimg: inconsistent pixels size")
   134  	}
   135  	img := &Gray64{pix, 8 * w, rect, 0, 0}
   136  	min := +math.MaxFloat64
   137  	max := -math.MaxFloat64
   138  	for i := 0; i < len(img.Pix); i += 8 {
   139  		v := img.at(i)
   140  		if v > max {
   141  			max = v
   142  		}
   143  		if v < min {
   144  			min = v
   145  		}
   146  	}
   147  	img.Min = min
   148  	img.Max = max
   149  	return img
   150  
   151  }
   152  
   153  func (p *Gray64) at(i int) float64 {
   154  	bits := binary.BigEndian.Uint64(p.Pix[i : i+8])
   155  	return math.Float64frombits(bits)
   156  }
   157  
   158  func (p *Gray64) setf(i int, v float64) {
   159  	binary.BigEndian.PutUint64(p.Pix[i:i+8], math.Float64bits(v))
   160  }
   161  
   162  func (p *Gray64) ColorModel() color.Model { return Gray64Model }
   163  func (p *Gray64) Bounds() image.Rectangle { return p.Rect }
   164  func (p *Gray64) At(x, y int) color.Color {
   165  	i := p.PixOffset(x, y)
   166  	v := p.at(i)
   167  	f := (1 - (v-p.Min)/(p.Max-p.Min))
   168  	switch {
   169  	case f < 0:
   170  		f = 0
   171  	case f > 1:
   172  		f = 1
   173  	}
   174  	return f64Gray(f)
   175  }
   176  
   177  func (p *Gray64) Set(x, y int, c color.Color) {
   178  	i := p.PixOffset(x, y)
   179  	r, _, _, _ := Gray64Model.Convert(c).RGBA()
   180  	v := math.Exp(math.Log(float64(r)/float64(0xffff)) / gamma)
   181  	p.setf(i, v)
   182  }
   183  
   184  func (p *Gray64) PixOffset(x, y int) int {
   185  	return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*8
   186  }
   187  
   188  // Models for the fltimg color types.
   189  var (
   190  	Gray32Model color.Model = color.ModelFunc(gray32Model)
   191  	Gray64Model color.Model = color.ModelFunc(gray64Model)
   192  )
   193  
   194  func gray32Model(c color.Color) color.Color {
   195  	if _, ok := c.(f32Gray); ok {
   196  		return c
   197  	}
   198  	r, g, b, _ := c.RGBA()
   199  	y := (19595*r + 38470*g + 7471*b + 1<<15) >> 16
   200  	v := math.Exp(math.Log(float64(y)/float64(0xffff)) / gamma)
   201  	return f32Gray(v)
   202  }
   203  
   204  func gray64Model(c color.Color) color.Color {
   205  	if _, ok := c.(f64Gray); ok {
   206  		return c
   207  	}
   208  	r, g, b, _ := c.RGBA()
   209  	y := (19595*r + 38470*g + 7471*b + 1<<15) >> 16
   210  	v := math.Exp(math.Log(float64(y)/float64(0xffff)) / gamma)
   211  	return f64Gray(v)
   212  }