eclipse

package
v0.0.0-...-55d3409 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 17, 2023 License: Apache-2.0 Imports: 24 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DebugPixels = []image.Point{} // Things in here get dumped in detail
View Source
var (
	Tonemappers = []string{"drago03", "durand", "fattal02", "icam06", "linear", "reinhard05"}
)

Functions

func AlignLayer

func AlignLayer(cfg Config, l1, l2 *Layer)

AlignLayer figures out the transform that aligns `l2` to `l1`. it then uses it to generate l2.Image, which will be pixel-aligned with l1.Image.

func ColToGrayU16

func ColToGrayU16(c color.Color) uint16

Col2GrayU16 maps a color into a gray value in the range [0, 0xFFFF]. If we had more of a handle on the color, maybe we'd map it to XYZ and pick out the luminance; but this works just fine.

func DevelopByDNG

func DevelopByDNG(cfg Config, p *Pixel)

DevelopDNG follows the DNG spec's algorithm for mapping a CameraNative sensor reading into a camera-neutral XYZ(D50) color, and then into a standard sRGB(D65) output color. This requires data from the camera, that is written into the DNG files - AsShotNeutral (the white balance correction) - ForwardMatrix (the camera's color correction matrix)

func DevelopByLayer

func DevelopByLayer(cfg Config, p *Pixel)

DevelopAsLayer is for debugging - it colors the pixel based on which layer it came from. (White balances it too)

func DevelopByNone

func DevelopByNone(cfg Config, p *Pixel)

func DevelopByWhiteBalanceOnly

func DevelopByWhiteBalanceOnly(cfg Config, p *Pixel)

func FuseByAverage

func FuseByAverage(cfg Config, p *Pixel)

FuseByAverage averages the non-overexposed layers together. It produces poor results, with notable color fringes forming near the boundary of each layer's area.

func FuseByPickMostExposed

func FuseByPickMostExposed(cfg Config, p *Pixel)

FuseByPickMostExposed is the default algorithm for image fusion: look for the image that is most-exposed (i.e. has received the most photons and will thus have lowest noise), but not over-exposed at this pixel (e.g. no channel more than ~80%).

func FuseBySector

func FuseBySector(cfg Config, p *Pixel)

FuseBySector cuts up the image into pie slices, and simply picks a source layer based on which pie segment the pixel lies inside. It's useful for comparing the source images to see how well they've been aligned.

func GrowRectangle

func GrowRectangle(r image.Rectangle, p image.Point) image.Rectangle

func ImgDiff

func ImgDiff(cfg Config, l1, l2 *Layer, passName string, xform AlignmentTransform) float64

ImgDiff compares two images, and returns an error metric; the less similar, the higher the value. It figures out the difference in XYZ luminance for each pixel (after normalizing for EV differences), and returns the average per-lotsof-pixels difference across the set of compared pixels.

If the pixel in either image is too dim or too bright on any channel, it is ignored, so we only really compare the subset of corona pixels that both images have a reasonable exposure for.

func ListTonemappers

func ListTonemappers() string

func RectCenter

func RectCenter(b image.Rectangle) image.Point

func WritePNG

func WritePNG(img image.Image, filename string) error

Types

type AlignmentTransform

type AlignmentTransform struct {
	Name string

	TranslateByX    float64
	TranslateByY    float64
	RotationCenterX float64
	RotationCenterY float64
	RotateByDeg     float64

	ErrorMetric float64
}

An AlignmentTransform maps a pixel location in a later layer to a pixel location in the base layer, that corresponds to the same point in the sky.

If you use an equatorial mount, this is all redundant.

func AlignLayerFine

func AlignLayerFine(cfg Config, l1, l2 *Layer, baseXform AlignmentTransform) AlignmentTransform

AlignLayerFine tries a wide range of possible finetune xforms in parallel, to find out which one fits best (i.e. has lowest error metric).

func (AlignmentTransform) String

func (xform AlignmentTransform) String() string

func (AlignmentTransform) ToMatrix

func (at AlignmentTransform) ToMatrix() emath.Aff3

func (AlignmentTransform) XFormImage

func (xform AlignmentTransform) XFormImage(src image.Image) image.Image

type Config

type Config struct {
	Verbosity int

	ManualOverrideAsShotNeutral emath.Vec3 // A white/neutral color in camera native RGB space
	ManualOverrideForwardMatrix emath.Mat3 // Maps white-balanced camera native RGB into XYZ(D50).

	DoEclipseAlignment          bool
	DoFineTunedAlignment        bool
	OutputWidthInSolarDiameters float64

	Fuser          string
	Developer      string
	Tonemapper     string
	FuserLuminance float64 // a var used by the fuser

	Alignments map[string]AlignmentTransform

	// Values we figure out elsewhere, and put here for access by rest of app
	CameraWhite emath.Vec3 // From a DNG file Layer{}, or overrides
	CameraToPCS emath.Mat3 // From a DNG file Layer{}, or overrides
	InputArea   image.Rectangle
	OutputArea  image.Rectangle
}

func NewConfig

func NewConfig() Config

func (Config) AsYaml

func (c Config) AsYaml() string

func (Config) GetDeveloper

func (c Config) GetDeveloper() PixelFunc

func (Config) GetFuser

func (c Config) GetFuser() PixelFunc

type ExposureValue

type ExposureValue struct {
	ISO          int   // 100, 800, etc.
	ApertureX10  int   // f/5.6 is the integer 56.
	ShutterSpeed rat64 // 1/500, 1/1000, etc.
	EV           int   // The final EV value - https://en.wikipedia.org/wiki/Exposure_value

	// This is the only value used downstream; it is used to scale the
	// pixel values during image fusion.
	IlluminanceAtMaxExposure float64 // How many lux generate a channel exposure == 0xFFFF
}

An ExposureValue details how the photograph was exposed, and allows us to figure out how much physical illumination (cd/m^2) was hitting the sensor, given a pixel color from the image.

This type figures out an 'EV' value, basicaly how many 'stops'; but it rounds off the shutterspeed & aperture-fnumber to the values that are "whole" stops.

func (ExposureValue) String

func (ev ExposureValue) String() string

func (*ExposureValue) Validate

func (ev *ExposureValue) Validate() error

type FusedImage

type FusedImage struct {
	Config
	Layers []Layer // Ordered, ascending EV (descending "number of photons needed to fully expose")
	Pixels []Pixel
}

FusedImage holds the image layers, and fuses them into a single image. Implements the image.Image interface.

func NewFusedImage

func NewFusedImage() FusedImage

func (*FusedImage) AddLayer

func (fi *FusedImage) AddLayer(l Layer)

func (*FusedImage) Align

func (fi *FusedImage) Align()

Align does all the work to figure out how to align the various layers, and generates the final transformed image for each layer.

func (*FusedImage) ApplyTonemapper

func (fi *FusedImage) ApplyTonemapper(op tmo.ToneMappingOperator, name string)

func (FusedImage) At

func (fi FusedImage) At(x, y int) color.Color

func (FusedImage) Bounds

func (fi FusedImage) Bounds() image.Rectangle

func (*FusedImage) CalculateInputArea

func (fi *FusedImage) CalculateInputArea() image.Rectangle

func (FusedImage) ColorModel

func (fi FusedImage) ColorModel() color.Model

Implement image.Image

func (*FusedImage) Fuse

func (fi *FusedImage) Fuse()

Fuse looks at the various layers for each pixel, and figures out a final merged value for that pixel. There are a few algorithms to pick from. Then it normalizes the brightness, so each pixel has the same EV. Finally it does color development, white balance etc.

func (FusedImage) HDRAt

func (fi FusedImage) HDRAt(x, y int) hdrcolor.Color

Implement hdr.Image

func (*FusedImage) LoadFilesAndDirs

func (fi *FusedImage) LoadFilesAndDirs(args ...string) error

func (*FusedImage) Pix

func (fi *FusedImage) Pix(x, y int) Pixel

Pixel access

func (*FusedImage) PixRW

func (fi *FusedImage) PixRW(x, y int) *Pixel

func (*FusedImage) SetupTonemapper

func (fi *FusedImage) SetupTonemapper(name string) tmo.ToneMappingOperator

Tweak the tmo parameters to better handle eclipse photos. By default, they almost always overexpose on the small but important bright areas.

func (FusedImage) Size

func (fi FusedImage) Size() int

func (FusedImage) String

func (fi FusedImage) String() string

func (*FusedImage) Tonemap

func (fi *FusedImage) Tonemap()

func (*FusedImage) WriteToHDR

func (fi *FusedImage) WriteToHDR(filename string) error

WriteToHDR outputs a HDR image. You can load this into photoshop or other HDR tools.

type Layer

type Layer struct {
	LoadFilename string
	LoadedImage  image.Image // The original photo image

	// Data we exctract from image metadata
	ExposureValue            // The exposure value for the photo
	CameraWhite   emath.Vec3 // A white/neutral color for the photo, given the color temp / white balance
	CameraToPCS   emath.Mat3 // Maps camera native color to PCS (CIEXYZ(D50?), incl. white balancing

	// Data we compute
	LunarLimb          // Our guess at where the moon is in the photo
	AlignmentTransform // How to map a point from the base image into this image

	// _This_ image is aligned across layers, so a pixel at [x,y] relates to the same bit of sky on every layer
	image.Image
}

A Layer holds an image.Image loaded from an input file, with extra stuff that allow us to fuse the exposures

func (Layer) Filename

func (l Layer) Filename() string

func (Layer) String

func (l Layer) String() string

type LunarLimb

type LunarLimb struct {
	LuminalCenter image.Point     // The luminance-weighted "center" of the image. Hopefully will be inside the limb.
	Brightness    uint16          // A rough average of the brightness of the pixels in the limb (floodfill needs to know this)
	Bounds        image.Rectangle // A box around the limb
}

The LunarLimb is the shadow/outline of the moon. We identify it and use it as a starting point for aligning images.

func FindLunarLimb

func FindLunarLimb(cfg Config, img image.Image) LunarLimb

FindLunarLimb returns a Rectangle that bounds the lunar limb, the outline of the moon. This is a fairly dumb routine; it finds the centroid of all the luminance in the image, assumes that is inside the lunar limb, and then floodfills out until it sees some bright pixels.

func (LunarLimb) Center

func (ll LunarLimb) Center() image.Point

func (*LunarLimb) Grow

func (ll *LunarLimb) Grow(p image.Point)

func (LunarLimb) Radius

func (ll LunarLimb) Radius() int

type Pixel

type Pixel struct {
	OutputPos image.Point // In output coords
	RawInputs []color.Color
	In        []ecolor.CameraNative

	Fused         ecolor.CameraNative // The single CameraNative pixel fused from the source images
	DevelopedRGB  hdrcolor.RGB        // The white balanced, color-corrected HDR RGB value
	TonemappedRGB color.Color         // The final LDR output, after HDR->LDR tonemapping

	LayerNumber int // which layer used; or how many layers used
}

func (Pixel) String

func (p Pixel) String() string

type PixelFunc

type PixelFunc func(Config, *Pixel)

A PixelFunc mutates a pixel. There are two families of these functions: - FuseBy: examine LDR pixels from all layers to generate a single HDR pixel - DevelopBy: perform color correction to the HDR pixel prior to tonemapping

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL