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
This commit is contained in:
David Vogel 2019-11-05 02:31:19 +01:00
parent d76dc20936
commit 52e23df1ee
7 changed files with 111 additions and 42 deletions

View File

@ -3,15 +3,20 @@
"Fullscreen",
"Lanczos",
"Vogel",
"backbuffer",
"cheggaaa",
"downscaling",
"executables",
"gridify",
"hacky",
"hilbertify",
"kbinani",
"lowram",
"manifoldco",
"mapcap",
"nfnt",
"noita",
"prerender",
"schollz",
"tcnksm",
"xmax",

View File

@ -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
```

View File

@ -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
}

View File

@ -19,22 +19,14 @@ type MedianBlendedImage struct {
bounds image.Rectangle
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
}

View File

@ -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)
}

3
go.mod
View File

@ -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

14
go.sum
View File

@ -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=