mirror of
https://github.com/Myzel394/simple-seam-carving-in-go.git
synced 2025-06-18 23:45:31 +02:00
143 lines
2.7 KiB
Go
143 lines
2.7 KiB
Go
package imageutils
|
|
|
|
import (
|
|
"fmt"
|
|
"image"
|
|
"image/color"
|
|
)
|
|
|
|
type SeamNode struct {
|
|
// if null = root node
|
|
PreviosNode *SeamNode
|
|
X int
|
|
Y int
|
|
ThisCost uint
|
|
AccumulatedCosts uint
|
|
}
|
|
|
|
func (s SeamNode) String() string {
|
|
if s.PreviosNode == nil {
|
|
return fmt.Sprintf("(%d, %d; %d)", s.X, s.Y, s.AccumulatedCosts)
|
|
} else {
|
|
return fmt.Sprintf("(%d, %d; %d) -> %v", s.X, s.Y, s.AccumulatedCosts, s.PreviosNode)
|
|
}
|
|
}
|
|
|
|
func (s SeamNode) WriteSeamChainToImage(img *image.RGBA) {
|
|
img.Set(
|
|
s.X,
|
|
s.Y,
|
|
color.RGBA{255, 0, 0, 1},
|
|
)
|
|
|
|
if s.PreviosNode != nil {
|
|
s.PreviosNode.WriteSeamChainToImage(img)
|
|
}
|
|
}
|
|
|
|
type Position struct {
|
|
X int
|
|
Y int
|
|
}
|
|
|
|
type ImageSeams struct {
|
|
// map of <y row> -> SeamNodes
|
|
Seams [][]*SeamNode
|
|
}
|
|
|
|
func NewImageSeams() ImageSeams {
|
|
return ImageSeams{
|
|
Seams: make([][]*SeamNode, 0),
|
|
}
|
|
}
|
|
|
|
func (i *ImageSeams) CreateSeamsFromRectangle(rect image.Rectangle) {
|
|
for y := range rect.Max.Y {
|
|
rowSeams := make([]*SeamNode, 0)
|
|
|
|
for x := range rect.Max.X {
|
|
rowSeams = append(rowSeams, &SeamNode{
|
|
X: x,
|
|
Y: y,
|
|
})
|
|
}
|
|
|
|
i.Seams = append(i.Seams, rowSeams)
|
|
}
|
|
}
|
|
|
|
// Get the nodes above a given x, y coordinate.
|
|
// Get the left and right neighbors, if there are any.
|
|
func (i *ImageSeams) GetNodesAbove(x int, y int) []*SeamNode {
|
|
if y == 0 {
|
|
return nil
|
|
}
|
|
|
|
rowSeams := i.Seams[y-1]
|
|
|
|
seams := make([]*SeamNode, 0, 3)
|
|
|
|
// Left node
|
|
if x != 0 {
|
|
seams = append(seams, rowSeams[x-1])
|
|
}
|
|
// Middle node
|
|
seams = append(seams, rowSeams[x])
|
|
// Right node
|
|
if x < (len(rowSeams) - 1) {
|
|
seams = append(seams, rowSeams[x+1])
|
|
}
|
|
|
|
return seams
|
|
}
|
|
|
|
// Find the best node that's above the current one at `(x, y)`
|
|
func (i *ImageSeams) FindBestNodeAbove(x int, y int) *SeamNode {
|
|
nodes := i.GetNodesAbove(x, y)
|
|
lowestCostNode := nodes[0]
|
|
|
|
for _, node := range nodes {
|
|
if node.AccumulatedCosts < lowestCostNode.AccumulatedCosts {
|
|
lowestCostNode = node
|
|
}
|
|
}
|
|
|
|
return lowestCostNode
|
|
}
|
|
|
|
func (i *ImageSeams) CreateOptimizedRoutesForRow(y int) {
|
|
row := i.Seams[y]
|
|
|
|
for x, node := range row {
|
|
bestNode := i.FindBestNodeAbove(x, y)
|
|
|
|
node.PreviosNode = bestNode
|
|
node.AccumulatedCosts = bestNode.AccumulatedCosts + node.ThisCost
|
|
}
|
|
}
|
|
|
|
func (i *ImageSeams) CreateOptimizedRoutes() {
|
|
for y := 1; y < len(i.Seams); y++ {
|
|
i.CreateOptimizedRoutesForRow(y)
|
|
}
|
|
}
|
|
|
|
func (i *ImageSeams) GetLowestSeam() *SeamNode {
|
|
lastRow := i.Seams[len(i.Seams)-1]
|
|
lowestNode := lastRow[0]
|
|
|
|
for _, node := range lastRow {
|
|
if node.AccumulatedCosts < lowestNode.AccumulatedCosts {
|
|
lowestNode = node
|
|
}
|
|
}
|
|
|
|
return lowestNode
|
|
}
|
|
|
|
func (i *ImageSeams) SetCostForNode(x int, y int, cost uint) {
|
|
node := i.Seams[y][x]
|
|
node.ThisCost = cost
|
|
node.AccumulatedCosts = cost
|
|
}
|