mirror of
https://github.com/Dadido3/noita-mapcap.git
synced 2024-11-18 17:17:31 +00:00
Stitching improvements
- Add integer downscale parameter - Output information about the total bounding box - Build workload chunks in a hilbert spiral to reduce memory consumption
This commit is contained in:
parent
2700cdb05e
commit
91efa7b4ba
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -1,8 +1,11 @@
|
|||||||
{
|
{
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
|
"Lanczos",
|
||||||
"Vogel",
|
"Vogel",
|
||||||
|
"executables",
|
||||||
"gridify",
|
"gridify",
|
||||||
"hacky",
|
"hacky",
|
||||||
|
"hilbertify",
|
||||||
"kbinani",
|
"kbinani",
|
||||||
"mapcap",
|
"mapcap",
|
||||||
"noita",
|
"noita",
|
||||||
|
@ -39,4 +39,4 @@ If you use `noita_dev.exe`, you can enable the debug mode by pressing `F5`. Once
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[MIT](LICENSE)
|
[MIT](LICENSE)
|
||||||
|
@ -12,11 +12,15 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/nfnt/resize"
|
||||||
)
|
)
|
||||||
|
|
||||||
type imageTile struct {
|
type imageTile struct {
|
||||||
fileName string
|
fileName string
|
||||||
|
|
||||||
|
scaleDivider int // Downscales the coordinates and images on the fly.
|
||||||
|
|
||||||
offset image.Point // Correction offset of the image, so that it aligns pixel perfect with other images. Determined by image matching.
|
offset image.Point // Correction offset of the image, so that it aligns pixel perfect with other images. Determined by image matching.
|
||||||
|
|
||||||
image image.Image // Either a rectangle or an RGBA image. The bounds of this image are determined by the filename.
|
image image.Image // Either a rectangle or an RGBA image. The bounds of this image are determined by the filename.
|
||||||
@ -59,6 +63,10 @@ func (it *imageTile) GetImage() (*image.RGBA, error) {
|
|||||||
return &image.RGBA{}, err
|
return &image.RGBA{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if it.scaleDivider > 1 {
|
||||||
|
img = resize.Resize(uint(oldRect.Dx()), uint(oldRect.Dy()), img, resize.NearestNeighbor)
|
||||||
|
}
|
||||||
|
|
||||||
imgRGBA, ok := img.(*image.RGBA)
|
imgRGBA, ok := img.(*image.RGBA)
|
||||||
if !ok {
|
if !ok {
|
||||||
return &image.RGBA{}, fmt.Errorf("Expected an RGBA image, got %T instead", img)
|
return &image.RGBA{}, fmt.Errorf("Expected an RGBA image, got %T instead", img)
|
||||||
@ -72,7 +80,7 @@ func (it *imageTile) GetImage() (*image.RGBA, error) {
|
|||||||
// Free the image after some time
|
// Free the image after some time
|
||||||
go func() {
|
go func() {
|
||||||
for it.imageUsedFlag {
|
for it.imageUsedFlag {
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
it.imageUsedFlag = false
|
it.imageUsedFlag = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,9 +37,13 @@ type tilePairs map[tileAlignmentKeys]tileAlignment
|
|||||||
|
|
||||||
var regexFileParse = regexp.MustCompile(`^(-?\d+),(-?\d+).png$`)
|
var regexFileParse = regexp.MustCompile(`^(-?\d+),(-?\d+).png$`)
|
||||||
|
|
||||||
func loadImages(path string) ([]imageTile, error) {
|
func loadImages(path string, scaleDivider int) ([]imageTile, error) {
|
||||||
var imageTiles []imageTile
|
var imageTiles []imageTile
|
||||||
|
|
||||||
|
if scaleDivider < 1 {
|
||||||
|
return nil, fmt.Errorf("Invalid scale of %v", scaleDivider)
|
||||||
|
}
|
||||||
|
|
||||||
files, err := filepath.Glob(filepath.Join(inputPath, "*.png"))
|
files, err := filepath.Glob(filepath.Join(inputPath, "*.png"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -66,9 +70,10 @@ func loadImages(path string) ([]imageTile, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
imageTiles = append(imageTiles, imageTile{
|
imageTiles = append(imageTiles, imageTile{
|
||||||
fileName: file,
|
fileName: file,
|
||||||
image: image.Rect(x, y, x+width, y+height),
|
scaleDivider: scaleDivider,
|
||||||
imageMutex: &sync.RWMutex{},
|
image: image.Rect(x/scaleDivider, y/scaleDivider, (x+width)/scaleDivider, (y+height)/scaleDivider),
|
||||||
|
imageMutex: &sync.RWMutex{},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,7 +255,11 @@ func (tp tilePairs) Stitch(tiles []imageTile, destImage *image.RGBA) error {
|
|||||||
// StitchGrid calls stitch, but divides the workload into a grid of chunks.
|
// StitchGrid calls stitch, but divides the workload into a grid of chunks.
|
||||||
// Additionally it runs the workload multithreaded.
|
// Additionally it runs the workload multithreaded.
|
||||||
func (tp tilePairs) StitchGrid(tiles []imageTile, destImage *image.RGBA, gridSize int) (errResult error) {
|
func (tp tilePairs) StitchGrid(tiles []imageTile, destImage *image.RGBA, gridSize int) (errResult error) {
|
||||||
workloads := gridifyRectangle(destImage.Bounds(), gridSize)
|
//workloads := gridifyRectangle(destImage.Bounds(), gridSize)
|
||||||
|
workloads, err := hilbertifyRectangle(destImage.Bounds(), gridSize)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
bar := progressbar.New(len(workloads))
|
bar := progressbar.New(len(workloads))
|
||||||
bar.RenderBlank()
|
bar.RenderBlank()
|
||||||
|
@ -17,12 +17,22 @@ var inputPath = filepath.Join(".", "..", "..", "output")
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.Printf("Starting to read tile information at \"%v\"", inputPath)
|
log.Printf("Starting to read tile information at \"%v\"", inputPath)
|
||||||
tiles, err := loadImages(inputPath)
|
tiles, err := loadImages(inputPath, 2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
log.Printf("Got %v tiles", len(tiles))
|
log.Printf("Got %v tiles", len(tiles))
|
||||||
|
|
||||||
|
totalBounds := image.Rectangle{}
|
||||||
|
for i, tile := range tiles {
|
||||||
|
if i == 0 {
|
||||||
|
totalBounds = tile.Bounds()
|
||||||
|
} else {
|
||||||
|
totalBounds = totalBounds.Union(tile.Bounds())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Printf("Total size of the possible output space is %v", totalBounds)
|
||||||
|
|
||||||
/*profFile, err := os.Create("cpu.prof")
|
/*profFile, err := os.Create("cpu.prof")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("could not create CPU profile: %v", err)
|
log.Panicf("could not create CPU profile: %v", err)
|
||||||
@ -35,14 +45,14 @@ func main() {
|
|||||||
|
|
||||||
// TODO: Flags / Program arguments
|
// TODO: Flags / Program arguments
|
||||||
|
|
||||||
outputRect := image.Rect(-35000, -35000, 35000, 35000)
|
outputRect := image.Rect(-1900*2, -1900*2, 1900*2, 1900*2)
|
||||||
|
|
||||||
log.Printf("Creating output image with a size of %v", outputRect.Size())
|
log.Printf("Creating output image with a size of %v", outputRect.Size())
|
||||||
outputImage := image.NewRGBA(outputRect)
|
outputImage := image.NewRGBA(outputRect)
|
||||||
|
|
||||||
log.Printf("Stitching %v tiles into an image at %v", len(tiles), outputImage.Bounds())
|
log.Printf("Stitching %v tiles into an image at %v", len(tiles), outputImage.Bounds())
|
||||||
tp := make(tilePairs)
|
tp := make(tilePairs)
|
||||||
if err := tp.StitchGrid(tiles, outputImage, 1024); err != nil {
|
if err := tp.StitchGrid(tiles, outputImage, 512); err != nil {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,10 +11,13 @@ import (
|
|||||||
"image/color"
|
"image/color"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"golang.org/x/image/font"
|
"golang.org/x/image/font"
|
||||||
"golang.org/x/image/font/basicfont"
|
"golang.org/x/image/font/basicfont"
|
||||||
"golang.org/x/image/math/fixed"
|
"golang.org/x/image/math/fixed"
|
||||||
|
|
||||||
|
"github.com/google/hilbert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Source: https://gist.github.com/sergiotapia/7882944
|
// Source: https://gist.github.com/sergiotapia/7882944
|
||||||
@ -75,6 +78,31 @@ func gridifyRectangle(rect image.Rectangle, gridSize int) (result []image.Rectan
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hilbertifyRectangle(rect image.Rectangle, gridSize int) ([]image.Rectangle, error) {
|
||||||
|
grid := gridifyRectangle(rect, gridSize)
|
||||||
|
|
||||||
|
gridX := divideFloor(rect.Min.X, gridSize)
|
||||||
|
gridY := divideFloor(rect.Min.Y, gridSize)
|
||||||
|
|
||||||
|
// Size of the grid in chunks
|
||||||
|
gridWidth := divideCeil(rect.Max.X, gridSize) - divideFloor(rect.Min.X, gridSize)
|
||||||
|
gridHeight := divideCeil(rect.Max.Y, gridSize) - divideFloor(rect.Min.Y, gridSize)
|
||||||
|
|
||||||
|
s, err := hilbert.NewHilbert(int(math.Pow(2, math.Ceil(math.Log2(math.Max(float64(gridWidth), float64(gridHeight)))))))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(grid, func(i, j int) bool {
|
||||||
|
// Ignore out of range errors, as they shouldn't happen.
|
||||||
|
hilbertIndexA, _ := s.MapInverse(grid[i].Min.X/gridSize-gridX, grid[i].Min.Y/gridSize-gridY)
|
||||||
|
hilbertIndexB, _ := s.MapInverse(grid[j].Min.X/gridSize-gridX, grid[j].Min.Y/gridSize-gridY)
|
||||||
|
return hilbertIndexA < hilbertIndexB
|
||||||
|
})
|
||||||
|
|
||||||
|
return grid, nil
|
||||||
|
}
|
||||||
|
|
||||||
func drawLabel(img *image.RGBA, x, y int, label string) {
|
func drawLabel(img *image.RGBA, x, y int, label string) {
|
||||||
col := color.RGBA{200, 100, 0, 255}
|
col := color.RGBA{200, 100, 0, 255}
|
||||||
point := fixed.Point26_6{X: fixed.Int26_6(x * 64), Y: fixed.Int26_6(y * 64)}
|
point := fixed.Point26_6{X: fixed.Int26_6(x * 64), Y: fixed.Int26_6(y * 64)}
|
||||||
@ -105,7 +133,7 @@ func pointAbs(p image.Point) image.Point {
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// Integer division that rounds to the next integer towards negative infinity
|
// Integer division that rounds to the next integer towards negative infinity.
|
||||||
func divideFloor(a, b int) int {
|
func divideFloor(a, b int) int {
|
||||||
temp := a / b
|
temp := a / b
|
||||||
|
|
||||||
@ -116,7 +144,7 @@ func divideFloor(a, b int) int {
|
|||||||
return temp
|
return temp
|
||||||
}
|
}
|
||||||
|
|
||||||
// Integer division that rounds to the next integer towards positive infinity
|
// Integer division that rounds to the next integer towards positive infinity.
|
||||||
func divideCeil(a, b int) int {
|
func divideCeil(a, b int) int {
|
||||||
temp := a / b
|
temp := a / b
|
||||||
|
|
||||||
@ -126,3 +154,10 @@ func divideCeil(a, b int) int {
|
|||||||
|
|
||||||
return temp
|
return temp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func maxInt(x, y int) int {
|
||||||
|
if x > y {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
2
go.mod
2
go.mod
@ -5,8 +5,10 @@ go 1.13
|
|||||||
require (
|
require (
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 // indirect
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 // indirect
|
||||||
github.com/gen2brain/shm v0.0.0-20180314170312-6c18ff7f8b90 // indirect
|
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/kbinani/screenshot v0.0.0-20190719135742-f06580e30cdc
|
||||||
github.com/lxn/win v0.0.0-20190919090605-24c5960b03d8 // indirect
|
github.com/lxn/win v0.0.0-20190919090605-24c5960b03d8 // indirect
|
||||||
|
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
|
||||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8
|
||||||
)
|
)
|
||||||
|
4
go.sum
4
go.sum
@ -5,12 +5,16 @@ 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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/gen2brain/shm v0.0.0-20180314170312-6c18ff7f8b90 h1:QagTG5rauLt6pVVEhnVSrlIX4ifhVIZOwmw6x6D8TUw=
|
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/gen2brain/shm v0.0.0-20180314170312-6c18ff7f8b90/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo=
|
||||||
|
github.com/google/hilbert v0.0.0-20181122061418-320f2e35a565 h1:KBAlCAY6eLC44FiEwbzEbHnpVlw15iVM4ZK8QpRIp4U=
|
||||||
|
github.com/google/hilbert v0.0.0-20181122061418-320f2e35a565/go.mod h1:xn6EodFfRzV6j8NXQRPjngeHWlrpOrsZPKuuLRThU1k=
|
||||||
github.com/kbinani/screenshot v0.0.0-20190719135742-f06580e30cdc h1:kGFotla6Dyr6a2ILeExAHlttPgJtnoP/GIw2uVN/4h4=
|
github.com/kbinani/screenshot v0.0.0-20190719135742-f06580e30cdc h1:kGFotla6Dyr6a2ILeExAHlttPgJtnoP/GIw2uVN/4h4=
|
||||||
github.com/kbinani/screenshot v0.0.0-20190719135742-f06580e30cdc/go.mod h1:f8GY5V3lRzakvEyr49P7hHRYoHtPr8zvj/7JodCoRzw=
|
github.com/kbinani/screenshot v0.0.0-20190719135742-f06580e30cdc/go.mod h1:f8GY5V3lRzakvEyr49P7hHRYoHtPr8zvj/7JodCoRzw=
|
||||||
github.com/lxn/win v0.0.0-20190919090605-24c5960b03d8 h1:RVMGIuuNgrpGB7I79f6xfhGCkpN47IaEGh8VTM0p7Xc=
|
github.com/lxn/win v0.0.0-20190919090605-24c5960b03d8 h1:RVMGIuuNgrpGB7I79f6xfhGCkpN47IaEGh8VTM0p7Xc=
|
||||||
github.com/lxn/win v0.0.0-20190919090605-24c5960b03d8/go.mod h1:ouWl4wViUNh8tPSIwxTVMuS014WakR1hqvBc2I0bMoA=
|
github.com/lxn/win v0.0.0-20190919090605-24c5960b03d8/go.mod h1:ouWl4wViUNh8tPSIwxTVMuS014WakR1hqvBc2I0bMoA=
|
||||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
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/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/schollz/progressbar/v2 v2.14.0 h1:vo7bdkI9E4/CIk9DnL5uVIaybLQiVtiCC2vO+u9j5IM=
|
github.com/schollz/progressbar/v2 v2.14.0 h1:vo7bdkI9E4/CIk9DnL5uVIaybLQiVtiCC2vO+u9j5IM=
|
||||||
|
Loading…
Reference in New Issue
Block a user