From d76dc209366f325be2a3fe78225d1ebe7430b547 Mon Sep 17 00:00:00 2001 From: David Vogel Date: Mon, 4 Nov 2019 22:44:35 +0100 Subject: [PATCH] Add low RAM mode --- bin/stitch/medianBlendedImage.go | 78 ++++++++++++++++++++++++++++++++ bin/stitch/stitch.go | 18 ++++++-- 2 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 bin/stitch/medianBlendedImage.go diff --git a/bin/stitch/medianBlendedImage.go b/bin/stitch/medianBlendedImage.go new file mode 100644 index 0000000..1acc4c7 --- /dev/null +++ b/bin/stitch/medianBlendedImage.go @@ -0,0 +1,78 @@ +// Copyright (c) 2019 David Vogel +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +package main + +import ( + "image" + "image/color" +) + +// MedianBlendedImageRowHeight defines the height of the cached output image. +const MedianBlendedImageRowHeight = 256 + +// MedianBlendedImage combines several imageTile to a single RGBA image. +type MedianBlendedImage struct { + tiles []imageTile + bounds image.Rectangle + + cachedRow *image.RGBA +} + +// NewMedianBlendedImage creates a new image from several single image tiles. +func NewMedianBlendedImage(tiles []imageTile) *MedianBlendedImage { + totalBounds := image.Rectangle{} + for i, tile := range tiles { + if i == 0 { + totalBounds = tile.Bounds() + } else { + totalBounds = totalBounds.Union(tile.Bounds()) + } + } + + return &MedianBlendedImage{ + tiles: tiles, + bounds: totalBounds, + cachedRow: &image.RGBA{}, + } +} + +// ColorModel returns the Image's color model. +func (mbi *MedianBlendedImage) ColorModel() color.Model { + return color.RGBAModel +} + +// Bounds returns the domain for which At can return non-zero color. +// The bounds do not necessarily contain the point (0, 0). +func (mbi *MedianBlendedImage) Bounds() image.Rectangle { + return mbi.bounds +} + +// At returns the color of the pixel at (x, y). +// At(Bounds().Min.X, Bounds().Min.Y) returns the upper-left pixel of the grid. +// At(Bounds().Max.X-1, Bounds().Max.Y-1) returns the lower-right one. +func (mbi *MedianBlendedImage) At(x, y int) color.Color { + p := image.Point{x, y} + + if !p.In(mbi.cachedRow.Bounds()) { + // Need to create a new row image + rect := mbi.Bounds() + rect.Min.Y = divideFloor(y, MedianBlendedImageRowHeight) * MedianBlendedImageRowHeight + rect.Max.Y = rect.Min.Y + MedianBlendedImageRowHeight + + if !p.In(rect) { + return color.RGBA{} + } + + mbi.cachedRow = image.NewRGBA(rect) + + // TODO: Don't use hilbert curve here + if err := StitchGrid(mbi.tiles, mbi.cachedRow, 512); err != nil { + return color.RGBA{} + } + } + + return mbi.cachedRow.RGBAAt(x, y) +} diff --git a/bin/stitch/stitch.go b/bin/stitch/stitch.go index 5e81924..b29884d 100644 --- a/bin/stitch/stitch.go +++ b/bin/stitch/stitch.go @@ -24,6 +24,7 @@ var flagXMin = flag.Int("xmin", 0, "Left bound of the output rectangle. This coo var flagYMin = flag.Int("ymin", 0, "Upper bound of the output rectangle. This coordinate is included in the output.") var flagXMax = flag.Int("xmax", 0, "Right bound of the output rectangle. This coordinate is not included in the output.") var flagYMax = flag.Int("ymax", 0, "Lower bound of the output rectangle. This coordinate is not included in the output.") +var flagLowRAM = flag.Bool("lowram", true, "Reduces the needed ram drastically, at the expense of speed.") func main() { flag.Parse() @@ -153,12 +154,19 @@ func main() { *flagOutputPath = result } - log.Printf("Creating output image with a size of %v", outputRect.Size()) - outputImage := image.NewRGBA(outputRect) + var outputImage image.Image + if *flagLowRAM { + outputImage = NewMedianBlendedImage(tiles) + } else { + log.Printf("Creating output image with a size of %v", outputRect.Size()) + tempImage := image.NewRGBA(outputRect) - log.Printf("Stitching %v tiles into an image at %v", len(tiles), outputImage.Bounds()) - if err := StitchGrid(tiles, outputImage, 512); err != nil { - log.Panic(err) + log.Printf("Stitching %v tiles into an image at %v", len(tiles), outputImage.Bounds()) + if err := StitchGrid(tiles, tempImage, 512); err != nil { + log.Panic(err) + } + + outputImage = tempImage } log.Printf("Creating output file \"%v\"", "output.png")