From 52e23df1ee7711844dd3c5cf1957bfaa1874da59 Mon Sep 17 00:00:00 2001 From: David Vogel Date: Tue, 5 Nov 2019 02:31:19 +0100 Subject: [PATCH] Several stitcher updates - Use different progress bar - Add bounds parameter to MedianBlendedImage - Add progress to MedianBlendedImage - Replace the `-lowram` flag with `-prerender` - Update README.md --- .vscode/settings.json | 5 ++++ bin/stitch/README.md | 35 ++++++++++++++--------- bin/stitch/imagetiles.go | 20 +++++++------ bin/stitch/medianBlendedImage.go | 28 ++++++++++--------- bin/stitch/stitch.go | 48 ++++++++++++++++++++++++++++---- go.mod | 3 +- go.sum | 14 ++++++++++ 7 files changed, 111 insertions(+), 42 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index b4f785c..0c773be 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,15 +3,20 @@ "Fullscreen", "Lanczos", "Vogel", + "backbuffer", + "cheggaaa", + "downscaling", "executables", "gridify", "hacky", "hilbertify", "kbinani", + "lowram", "manifoldco", "mapcap", "nfnt", "noita", + "prerender", "schollz", "tcnksm", "xmax", diff --git a/bin/stitch/README.md b/bin/stitch/README.md index ef415a9..44c591e 100644 --- a/bin/stitch/README.md +++ b/bin/stitch/README.md @@ -26,8 +26,8 @@ example list of files: ## Usage -- Run the program and follow the interactive prompt. -- Run the program with parameters: +- Either run the program and follow the interactive prompt. +- Or run the program with parameters: - `divide int` A downscaling factor. 2 will produce an image with half the side lengths. (default 1) - `input string`The source path of the image tiles to be stitched. (default "..\\..\\output") @@ -41,22 +41,31 @@ example list of files: Lower bound of the output rectangle. This coordinate is not included in the output. - `ymin int` Upper bound of the output rectangle. This coordinate is included in the output. + - `prerender` + Pre renders the image in RAM before saving. Can speed things up if you have enough RAM. -Example of usage: +To output the 100x100 area that is centered at the origin use: ``` Shell Session -./stitch -divide 2 +./stitch -divide 1 -xmin -50 -xmax 50 -ymin -50 -ymax 50 ``` -Example of output: +To enter the parameters inside of the program: ``` Shell Session -2019/10/25 16:02:25 Starting to read tile information at "..\..\output" -2019/10/25 16:02:34 Got 43338 tiles -2019/10/25 16:02:34 Total size of the possible output space is (-19968,-36864)-(21184,35100) -2019/10/25 16:02:34 Creating output image with a size of (41152,71964) -2019/10/25 16:02:46 Stitching 43338 tiles into an image at (-19968,-36864)-(21184,35100) - 100% |████████████████████████████████████████| [33m13s:0s] -2019/10/25 16:35:59 Creating output file "output.png" -2019/10/25 16:44:17 Created output file "output.png" +./stitch +``` + +Example output: + +``` Shell Session +Enter downscaling factor:1 +Enter input path:..\..\output +2019/11/04 23:53:20 Starting to read tile information at "..\..\output" +2019/11/04 23:53:32 Got 20933 tiles +2019/11/04 23:53:32 Total size of the possible output space is (-25620,-36540)-(25620,36540) +Enter output rectangle (xMin,yMin;xMax,yMax):-25620,-36540;25620,36540 +Enter output filename and path:output.png +2019/11/04 23:53:35 Creating output file "output.png" +105 / 571 [--------------->____________________________________________________________________] 18.39% 1 p/s ETA 14m0s ``` diff --git a/bin/stitch/imagetiles.go b/bin/stitch/imagetiles.go index 6fdfe0f..9fb379f 100644 --- a/bin/stitch/imagetiles.go +++ b/bin/stitch/imagetiles.go @@ -16,7 +16,7 @@ import ( "strconv" "sync" - "github.com/schollz/progressbar/v2" + "github.com/cheggaaa/pb/v3" ) var regexFileParse = regexp.MustCompile(`^(-?\d+),(-?\d+).png$`) @@ -82,7 +82,9 @@ func Stitch(tiles []imageTile, destImage *image.RGBA) error { } imgCopy := *img imgCopy.Rect = imgCopy.Rect.Add(tile.offset).Inset(4) // Reduce image bounds by 4 pixels on each side, because otherwise there will be artifacts. - images = append(images, &imgCopy) // TODO: Fix transparent pixels at the output image border because of Inset + images = append(images, &imgCopy) + // TODO: Fix transparent pixels at the output image border because of inset + // TODO: Fix downscaled images to cause artifacts because of the inset } } @@ -104,15 +106,16 @@ func Stitch(tiles []imageTile, destImage *image.RGBA) error { // StitchGrid calls stitch, but divides the workload into a grid of chunks. // Additionally it runs the workload multithreaded. -func StitchGrid(tiles []imageTile, destImage *image.RGBA, gridSize int) (errResult error) { +func StitchGrid(tiles []imageTile, destImage *image.RGBA, gridSize int, bar *pb.ProgressBar) (errResult error) { //workloads := gridifyRectangle(destImage.Bounds(), gridSize) workloads, err := hilbertifyRectangle(destImage.Bounds(), gridSize) if err != nil { return err } - bar := progressbar.New(len(workloads)) - bar.RenderBlank() + if bar != nil { + bar.SetTotal(int64(len(workloads))).Start() + } // Start worker threads wc := make(chan image.Rectangle) @@ -125,7 +128,9 @@ func StitchGrid(tiles []imageTile, destImage *image.RGBA, gridSize int) (errResu if err := Stitch(tiles, destImage.SubImage(workload).(*image.RGBA)); err != nil { errResult = err // This will not stop execution, but at least one of any errors is returned. } - bar.Add(1) + if bar != nil { + bar.Increment() + } } }() } @@ -139,9 +144,6 @@ func StitchGrid(tiles []imageTile, destImage *image.RGBA, gridSize int) (errResu close(wc) wg.Wait() - // Newline because of the progress bar - fmt.Println("") - return } diff --git a/bin/stitch/medianBlendedImage.go b/bin/stitch/medianBlendedImage.go index 1acc4c7..d51b608 100644 --- a/bin/stitch/medianBlendedImage.go +++ b/bin/stitch/medianBlendedImage.go @@ -18,23 +18,15 @@ type MedianBlendedImage struct { tiles []imageTile bounds image.Rectangle - cachedRow *image.RGBA + cachedRow *image.RGBA + queryCounter int } // 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()) - } - } - +func NewMedianBlendedImage(tiles []imageTile, bounds image.Rectangle) *MedianBlendedImage { return &MedianBlendedImage{ tiles: tiles, - bounds: totalBounds, + bounds: bounds, cachedRow: &image.RGBA{}, } } @@ -56,6 +48,9 @@ func (mbi *MedianBlendedImage) Bounds() image.Rectangle { func (mbi *MedianBlendedImage) At(x, y int) color.Color { p := image.Point{x, y} + // Assume that every pixel is only queried once + mbi.queryCounter++ + if !p.In(mbi.cachedRow.Bounds()) { // Need to create a new row image rect := mbi.Bounds() @@ -69,10 +64,17 @@ func (mbi *MedianBlendedImage) At(x, y int) color.Color { mbi.cachedRow = image.NewRGBA(rect) // TODO: Don't use hilbert curve here - if err := StitchGrid(mbi.tiles, mbi.cachedRow, 512); err != nil { + if err := StitchGrid(mbi.tiles, mbi.cachedRow, 512, nil); err != nil { return color.RGBA{} } } return mbi.cachedRow.RGBAAt(x, y) } + +// Progress returns the approximate progress of any process that scans the image from top to bottom. +func (mbi *MedianBlendedImage) Progress() (value, max int) { + size := mbi.Bounds().Size() + + return mbi.queryCounter, size.X * size.Y +} diff --git a/bin/stitch/stitch.go b/bin/stitch/stitch.go index b29884d..5be0e01 100644 --- a/bin/stitch/stitch.go +++ b/bin/stitch/stitch.go @@ -13,7 +13,10 @@ import ( "log" "os" "path/filepath" + "sync" + "time" + "github.com/cheggaaa/pb/v3" "github.com/manifoldco/promptui" ) @@ -24,7 +27,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.") +var flagPrerender = flag.Bool("prerender", false, "Pre renders the image in RAM before saving. Can speed things up if you have enough RAM.") func main() { flag.Parse() @@ -155,16 +158,44 @@ func main() { } var outputImage image.Image - if *flagLowRAM { - outputImage = NewMedianBlendedImage(tiles) - } else { + bar := pb.Full.New(0) + var wg sync.WaitGroup + done := make(chan bool) + + if *flagPrerender { 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, tempImage, 512); err != nil { + log.Printf("Stitching %v tiles into an image at %v", len(tiles), tempImage.Bounds()) + if err := StitchGrid(tiles, tempImage, 512, bar); err != nil { log.Panic(err) } + bar.Finish() + + outputImage = tempImage + } else { + tempImage := NewMedianBlendedImage(tiles, outputRect) + _, max := tempImage.Progress() + bar.SetTotal(int64(max)).Start().SetRefreshRate(1 * time.Second) + + wg.Add(1) + go func() { + defer wg.Done() + + ticker := time.NewTicker(1 * time.Second) + for { + select { + case <-done: + value, _ := tempImage.Progress() + bar.SetCurrent(int64(value)) + bar.Finish() + return + case <-ticker.C: + value, _ := tempImage.Progress() + bar.SetCurrent(int64(value)) + } + } + }() outputImage = tempImage } @@ -180,6 +211,11 @@ func main() { log.Panic(err) } + if !*flagPrerender { + done <- true + wg.Wait() + } + if err := f.Close(); err != nil { log.Panic(err) } diff --git a/go.mod b/go.mod index 29b0308..8a2db0f 100644 --- a/go.mod +++ b/go.mod @@ -5,13 +5,14 @@ go 1.13 require ( github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 // indirect github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect + github.com/cheggaaa/pb/v3 v3.0.2 github.com/gen2brain/shm v0.0.0-20180314170312-6c18ff7f8b90 // indirect github.com/google/hilbert v0.0.0-20181122061418-320f2e35a565 github.com/kbinani/screenshot v0.0.0-20190719135742-f06580e30cdc github.com/lxn/win v0.0.0-20190919090605-24c5960b03d8 // indirect github.com/manifoldco/promptui v0.3.2 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 - github.com/schollz/progressbar/v2 v2.14.0 + github.com/schollz/progressbar/v2 v2.14.0 // indirect github.com/tcnksm/go-input v0.0.0-20180404061846-548a7d7a8ee8 // indirect golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c // indirect diff --git a/go.sum b/go.sum index cb4adb5..b47f97b 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,14 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= +github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= github.com/alecthomas/gometalinter v2.0.11+incompatible h1:ENdXMllZNSVDTJUUVIzBW9CSEpntTrQa76iRsEFLX/M= github.com/alecthomas/gometalinter v2.0.11+incompatible/go.mod h1:qfIpQGGz3d+NmgyPBqv+LSh50emm1pt72EtcX2vKYQk= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/cheggaaa/pb v2.0.7+incompatible h1:gLKifR1UkZ/kLkda5gC0K6c8g+jU2sINPtBeOiNlMhU= +github.com/cheggaaa/pb/v3 v3.0.2 h1:/u+zw5RBzW1CxRpVIqrZv4PpZpN+yaRPdsRORKyDjv4= +github.com/cheggaaa/pb/v3 v3.0.2/go.mod h1:SqqeMF/pMOIu3xgGoxtPYhMNQP258xE4x/XRTYua+KU= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= @@ -15,6 +20,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/gen2brain/shm v0.0.0-20180314170312-6c18ff7f8b90 h1:QagTG5rauLt6pVVEhnVSrlIX4ifhVIZOwmw6x6D8TUw= github.com/gen2brain/shm v0.0.0-20180314170312-6c18ff7f8b90/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo= github.com/golang/lint v0.0.0-20181026193005-c67002cb31c3 h1:I4BOK3PBMjhWfQM2zPJKK7lOBGsrsvOB7kBELP33hiE= @@ -42,8 +49,14 @@ github.com/manifoldco/promptui v0.3.2 h1:rir7oByTERac6jhpHUPErHuopoRDvO3jxS+Fdad github.com/manifoldco/promptui v0.3.2/go.mod h1:8JU+igZ+eeiiRku4T5BjtKh2ms8sziGpSYl1gN8Bazw= github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= @@ -68,6 +81,7 @@ golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+o golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 h1:x/bBzNauLQAlE3fLku/xy92Y8QwKX5HZymrMz2IiKFc= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=