diff --git a/.vscode/settings.json b/.vscode/settings.json index 4d7a6f2..73d2a7a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -39,6 +39,7 @@ "tcnksm", "tdewolff", "Vogel", + "Voronoi", "xmax", "xmin", "ymax", diff --git a/bin/stitch/blend-methods.go b/bin/stitch/blend-methods.go index a5cd531..812dcbb 100644 --- a/bin/stitch/blend-methods.go +++ b/bin/stitch/blend-methods.go @@ -8,6 +8,7 @@ package main import ( "image" "image/color" + "math" "sort" ) @@ -82,3 +83,67 @@ func (b BlendMethodMedian) Draw(tiles []*ImageTile, destImage *image.RGBA) { } } } + +// BlendMethodVoronoi maps every pixel to the tile with the closest center point distance. +// The result is basically a Voronoi partitioning. +type BlendMethodVoronoi struct { + BlendTileLimit int // If larger than 0, limits blending to the n newest tiles by file modification time. +} + +// Draw implements the StitchedImageBlendMethod interface. +func (b BlendMethodVoronoi) Draw(tiles []*ImageTile, destImage *image.RGBA) { + bounds := destImage.Bounds() + + if b.BlendTileLimit > 0 { + // Sort tiles by date. + sort.Slice(tiles, func(i, j int) bool { return tiles[i].modTime.After(tiles[j].modTime) }) + } + + // List of images corresponding to the "tiles" list. + // Can contain empty/nil entries for images that failed to load. + images := []*image.RGBA{} + for _, tile := range tiles { + images = append(images, tile.GetImage()) + } + + // Create arrays to be reused every pixel. + var col color.RGBA + var centerDistSqrMin int + + for iy := bounds.Min.Y; iy < bounds.Max.Y; iy++ { + for ix := bounds.Min.X; ix < bounds.Max.X; ix++ { + point := image.Point{ix, iy} + count := 0 + centerDistSqrMin = math.MaxInt + + // Iterate through all images and create a list of colors. + for _, img := range images { + if img != nil { + if point.In(img.Bounds()) { + center := img.Bounds().Min.Add(img.Bounds().Max).Div(2) + centerDiff := point.Sub(center) + distSqr := centerDiff.X*centerDiff.X + centerDiff.Y*centerDiff.Y + if centerDistSqrMin > distSqr { + centerDistSqrMin = distSqr + col = img.RGBAAt(point.X, point.Y) + } + count++ + // Limit number of tiles to blend. + // Will be ignored if the blend tile limit is 0. + if count == b.BlendTileLimit { + break + } + } + } + } + + // If there were no images to get data from, ignore the pixel. + if count == 0 { + continue + } + + col.A = 255 + destImage.SetRGBA(ix, iy, col) + } + } +}