Browse Source

init

master
bake 11 months ago
commit
1c767e2213
3 changed files with 146 additions and 0 deletions
  1. 3
    0
      go.mod
  2. 4
    0
      go.sum
  3. 139
    0
      transform.go

+ 3
- 0
go.mod View File

@@ -0,0 +1,3 @@
module github.com/bakerolls/transform

require gonum.org/v1/gonum v0.0.0-20181120223212-d752f8fef7d8

+ 4
- 0
go.sum View File

@@ -0,0 +1,4 @@
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gonum.org/v1/gonum v0.0.0-20181120223212-d752f8fef7d8 h1:X6q/vov/Og/pAS/mfk3zOWpbkMG5as3CUf8PjiN6+wY=
gonum.org/v1/gonum v0.0.0-20181120223212-d752f8fef7d8/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=

+ 139
- 0
transform.go View File

@@ -0,0 +1,139 @@
package transform

import (
"image"
"math"

"gonum.org/v1/gonum/mat"
)

// Translate an image.
func Translate(img image.Image, x, y int) *image.RGBA {
return Matrix(img, translate(x, y))
}

func translate(x, y int) mat.Matrix {
return mat.NewDense(3, 3, []float64{
1, 0, float64(x),
0, 1, float64(y),
0, 0, 1,
})
}

// Rotate an image.
func Rotate(img image.Image, deg float64) *image.RGBA {
b := img.Bounds()
w, h := b.Max.X-b.Min.X, b.Max.Y-b.Min.Y
return Matrix(img, mul(
translate(w/2, h/2),
rotate(deg),
translate(-w/2, -h/2),
))
}

// RotateInBounds rotates an image and scales the result to fit inside the
// original bounding box.
func RotateInBounds(img image.Image, deg float64) *image.RGBA {
b := img.Bounds()
w, h := float64(b.Max.X-b.Min.X), float64(b.Max.Y-b.Min.Y)
if math.Mod(math.Abs(deg), 180) >= 90 {
w, h = h, w
}
rad := math.Abs(math.Mod(deg, 90)) * (math.Pi / 180)
rotW := w*math.Cos(rad) + h*math.Sin(rad)
rotH := w*math.Sin(rad) + h*math.Cos(rad)
if math.Mod(math.Abs(deg), 180) >= 90 {
w, h = h, w
}
s := math.Min(w/rotW, h/rotH)
return Matrix(img, mul(
translate(int(w/2), int(h/2)),
scale(s, s),
rotate(deg),
translate(-int(w/2), -int(h/2)),
))
}

func rotate(deg float64) mat.Matrix {
deg *= math.Pi / 180
return mat.NewDense(3, 3, []float64{
math.Cos(deg), -math.Sin(deg), 0,
math.Sin(deg), math.Cos(deg), 0,
0, 0, 1,
})
}

// Scale an image
func Scale(img image.Image, x, y float64) *image.RGBA {
b := img.Bounds()
w, h := b.Max.X-b.Min.X, b.Max.Y-b.Min.Y
return Matrix(img, mul(
translate(int(w/2), int(h/2)),
scale(x, y),
translate(-int(w/2), -int(h/2)),
))
}

func scale(x, y float64) mat.Matrix {
return mat.NewDense(3, 3, []float64{
x, 0, 0,
0, y, 0,
0, 0, 1,
})
}

// FlipH flips an image horizontally.
func FlipH(img image.Image) *image.RGBA {
return Scale(img, -1, 1)
}

// FlipV flips an image vertically.
func FlipV(img image.Image) *image.RGBA {
return Scale(img, 1, -1)
}

// Shear an image.
func Shear(img image.Image, x, y float64) *image.RGBA {
return Matrix(img, shear(x, y))
}

func shear(x, y float64) mat.Matrix {
return mat.NewDense(3, 3, []float64{
1, x, 0,
y, 1, 0,
0, 0, 1,
})
}

// Matrix executes a transformation matrix on an image.
func Matrix(img image.Image, m mat.Matrix) *image.RGBA {
b := img.Bounds()
dst := image.NewRGBA(b)
for x := b.Min.X; x < b.Max.X; x++ {
for y := b.Min.Y; y < b.Max.Y; y++ {
x2, y2 := mulXY(x, y, m)
dst.Set(x2, y2, img.At(x, y))
}
}
return dst
}

// mul multiplies matrices.
func mul(m mat.Matrix, ms ...mat.Matrix) mat.Matrix {
r, c := m.Dims()
n := mat.NewDense(r, c, make([]float64, r*c))
n.Add(n, m)
for _, m := range ms {
n.Mul(n, m)
}
return n
}

// mulXY multiplies a new matrix based on (x, y) with a given one, returning
// (x', y').
func mulXY(x, y int, a mat.Matrix) (int, int) {
b := mat.NewDense(3, 1, []float64{float64(x), float64(y), 1})
var c mat.Dense
c.Mul(a, b)
return int(math.Round(c.At(0, 0))), int(math.Round(c.At(1, 0)))
}

Loading…
Cancel
Save