|
|
@@ -19,6 +19,7 @@ package resize
|
|
|
import (
|
|
|
"image"
|
|
|
"image/color"
|
|
|
+ "math"
|
|
|
)
|
|
|
|
|
|
// color.RGBA64 as array
|
|
|
@@ -34,81 +35,24 @@ func clampToUint16(x float32) (y uint16) {
|
|
|
y = uint16(x)
|
|
|
if x < 0 {
|
|
|
y = 0
|
|
|
- } else if x > float32(0xffff) {
|
|
|
+ } else if x > float32(0xfffe) {
|
|
|
y = 0xffff
|
|
|
}
|
|
|
return
|
|
|
}
|
|
|
|
|
|
-// Nearest-neighbor interpolation.
|
|
|
-// Approximates a value by returning the value of the nearest point.
|
|
|
-func NearestNeighbor(x, y float32, img image.Image) color.RGBA64 {
|
|
|
- xn, yn := int(float32(int(x))+0.5), int(float32(int(y))+0.5)
|
|
|
- c := toRGBA(img.At(xn, yn))
|
|
|
- return color.RGBA64{c[0], c[1], c[2], c[3]}
|
|
|
-}
|
|
|
-
|
|
|
-// Linear interpolation.
|
|
|
-func linearInterp(x float32, p *[2]RGBA) (c RGBA) {
|
|
|
- x -= float32(int(x))
|
|
|
- for i := range c {
|
|
|
- c[i] = clampToUint16(float32(p[0][i])*(1.0-x) + x*float32(p[1][i]))
|
|
|
- }
|
|
|
- return
|
|
|
-}
|
|
|
-
|
|
|
-// Bilinear interpolation.
|
|
|
-func Bilinear(x, y float32, img image.Image) color.RGBA64 {
|
|
|
- xf, yf := int(x), int(y)
|
|
|
-
|
|
|
- var row [2]RGBA
|
|
|
- var col [2]RGBA
|
|
|
- for i := 0; i < 2; i++ {
|
|
|
- row = [2]RGBA{toRGBA(img.At(xf, yf+i)), toRGBA(img.At(xf+1, yf+i))}
|
|
|
- col[i] = linearInterp(x, &row)
|
|
|
- }
|
|
|
-
|
|
|
- c := linearInterp(y, &col)
|
|
|
- return color.RGBA64{c[0], c[1], c[2], c[3]}
|
|
|
-}
|
|
|
-
|
|
|
-// cubic interpolation
|
|
|
-func cubicInterp(x float32, p *[4]RGBA) (c RGBA) {
|
|
|
- x -= float32(int(x))
|
|
|
- for i := range c {
|
|
|
- c[i] = clampToUint16(float32(p[1][i]) + 0.5*x*(float32(p[2][i])-float32(p[0][i])+x*(2.0*float32(p[0][i])-5.0*float32(p[1][i])+4.0*float32(p[2][i])-float32(p[3][i])+x*(3.0*(float32(p[1][i])-float32(p[2][i]))+float32(p[3][i])-float32(p[0][i])))))
|
|
|
- }
|
|
|
- return
|
|
|
-}
|
|
|
-
|
|
|
-// Bicubic interpolation.
|
|
|
-func Bicubic(x, y float32, img image.Image) color.RGBA64 {
|
|
|
- xf, yf := int(x), int(y)
|
|
|
-
|
|
|
- var row [4]RGBA
|
|
|
- var col [4]RGBA
|
|
|
- for i := 0; i < 4; i++ {
|
|
|
- row = [4]RGBA{toRGBA(img.At(xf-1, yf+i-1)), toRGBA(img.At(xf, yf+i-1)), toRGBA(img.At(xf+1, yf+i-1)), toRGBA(img.At(xf+2, yf+i-1))}
|
|
|
- col[i] = cubicInterp(x, &row)
|
|
|
- }
|
|
|
-
|
|
|
- c := cubicInterp(y, &col)
|
|
|
- return color.RGBA64{c[0], c[1], c[2], c[3]}
|
|
|
-}
|
|
|
-
|
|
|
-// 1-d convolution with windowed sinc for a=2.
|
|
|
-func lanczos2_x(x float32, p *[4]RGBA) (c RGBA) {
|
|
|
+func convolution1d(x float32, kernel func(float32, int) float32, p []RGBA) (c RGBA) {
|
|
|
x -= float32(int(x))
|
|
|
|
|
|
- var kernel float32
|
|
|
- var sum float32 = 0 // for kernel normalization
|
|
|
+ var k float32
|
|
|
+ var sum float32 = 0
|
|
|
l := [4]float32{0.0, 0.0, 0.0, 0.0}
|
|
|
|
|
|
for j := range p {
|
|
|
- kernel = float32(Sinc(float64(x-float32(j-1)))) * float32(Sinc(float64((x-float32(j-1))/2.0)))
|
|
|
- sum += kernel
|
|
|
+ k = kernel(x, j)
|
|
|
+ sum += k
|
|
|
for i := range c {
|
|
|
- l[i] += float32(p[j][i]) * kernel
|
|
|
+ l[i] += float32(p[j][i]) * k
|
|
|
}
|
|
|
}
|
|
|
for i := range c {
|
|
|
@@ -117,53 +61,77 @@ func lanczos2_x(x float32, p *[4]RGBA) (c RGBA) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
-// Lanczos interpolation (a=2).
|
|
|
-func Lanczos2(x, y float32, img image.Image) color.RGBA64 {
|
|
|
- xf, yf := int(x), int(y)
|
|
|
+func filter(x, y float32, img image.Image, n int, kernel func(x float32, j int) float32) color.RGBA64 {
|
|
|
+ xf, yf := int(x)-n/2+1, int(y)-n/2+1
|
|
|
|
|
|
- var row [4]RGBA
|
|
|
- var col [4]RGBA
|
|
|
- for i := range row {
|
|
|
- row = [4]RGBA{toRGBA(img.At(xf-1, yf+i-1)), toRGBA(img.At(xf, yf+i-1)), toRGBA(img.At(xf+1, yf+i-1)), toRGBA(img.At(xf+2, yf+i-1))}
|
|
|
- col[i] = lanczos2_x(x, &row)
|
|
|
+ row := make([]RGBA, n)
|
|
|
+ col := make([]RGBA, n)
|
|
|
+
|
|
|
+ for i := 0; i < n; i++ {
|
|
|
+ for j := 0; j < n; j++ {
|
|
|
+ row[j] = toRGBA(img.At(xf+j, yf+i))
|
|
|
+ }
|
|
|
+ col[i] = convolution1d(x, kernel, row)
|
|
|
}
|
|
|
|
|
|
- c := lanczos2_x(y, &col)
|
|
|
+ c := convolution1d(y, kernel, col)
|
|
|
return color.RGBA64{c[0], c[1], c[2], c[3]}
|
|
|
}
|
|
|
|
|
|
-// 1-d convolution with windowed sinc for a=3.
|
|
|
-func lanczos3_x(x float32, p *[6]RGBA) (c RGBA) {
|
|
|
- x -= float32(int(x))
|
|
|
+// Nearest-neighbor interpolation.
|
|
|
+// Approximates a value by returning the value of the nearest point.
|
|
|
+func NearestNeighbor(x, y float32, img image.Image) color.RGBA64 {
|
|
|
+ n := 2
|
|
|
+ kernel := func(x float32, j int) (y float32) {
|
|
|
+ if x+0.5 >= float32(j) && x+0.5 < float32(j)+1 {
|
|
|
+ y = 1
|
|
|
+ } else {
|
|
|
+ y = 0
|
|
|
+ }
|
|
|
+ return
|
|
|
+ }
|
|
|
+ return filter(x, y, img, n, kernel)
|
|
|
+}
|
|
|
|
|
|
- var kernel float32
|
|
|
- var sum float32 = 0 // for kernel normalization
|
|
|
- l := [4]float32{0.0, 0.0, 0.0, 0.0}
|
|
|
+// Bicubic interpolation
|
|
|
+func Bilinear(x, y float32, img image.Image) color.RGBA64 {
|
|
|
+ n := 2
|
|
|
+ kernel := func(x float32, j int) float32 {
|
|
|
+ xa := float32(math.Abs(float64(x - float32(j))))
|
|
|
+ return 1 - xa
|
|
|
+ }
|
|
|
+ return filter(x, y, img, n, kernel)
|
|
|
+}
|
|
|
|
|
|
- for j := range p {
|
|
|
- kernel = float32(Sinc(float64(x-float32(j-2)))) * float32(Sinc(float64((x-float32(j-2))/3.0)))
|
|
|
- sum += kernel
|
|
|
- for i := range c {
|
|
|
- l[i] += float32(p[j][i]) * kernel
|
|
|
+// Bicubic interpolation
|
|
|
+func Bicubic(x, y float32, img image.Image) color.RGBA64 {
|
|
|
+ n := 4
|
|
|
+ kernel := func(x float32, j int) (y float32) {
|
|
|
+ xa := float32(math.Abs(float64(x - float32(j-1))))
|
|
|
+ if xa <= 1 {
|
|
|
+ y = 1.5*xa*xa*xa - 2.5*xa*xa + 1
|
|
|
+ } else {
|
|
|
+ y = -0.5*xa*xa*xa + 2.5*xa*xa - 4*xa + 2
|
|
|
}
|
|
|
+ return
|
|
|
}
|
|
|
- for i := range c {
|
|
|
- c[i] = clampToUint16(l[i] / sum)
|
|
|
+ return filter(x, y, img, n, kernel)
|
|
|
+}
|
|
|
+
|
|
|
+// Lanczos interpolation (a=2).
|
|
|
+func Lanczos2(x, y float32, img image.Image) color.RGBA64 {
|
|
|
+ n := 4
|
|
|
+ kernel := func(x float32, j int) float32 {
|
|
|
+ return float32(Sinc(float64(x-float32(j-1)))) * float32(Sinc(float64((x-float32(j-1))/float32(2))))
|
|
|
}
|
|
|
- return
|
|
|
+ return filter(x, y, img, n, kernel)
|
|
|
}
|
|
|
|
|
|
// Lanczos interpolation (a=3).
|
|
|
func Lanczos3(x, y float32, img image.Image) color.RGBA64 {
|
|
|
- xf, yf := int(x), int(y)
|
|
|
-
|
|
|
- var row [6]RGBA
|
|
|
- var col [6]RGBA
|
|
|
- for i := range row {
|
|
|
- row = [6]RGBA{toRGBA(img.At(xf-2, yf+i-2)), toRGBA(img.At(xf-1, yf+i-2)), toRGBA(img.At(xf, yf+i-2)), toRGBA(img.At(xf+1, yf+i-2)), toRGBA(img.At(xf+2, yf+i-2)), toRGBA(img.At(xf+3, yf+i-2))}
|
|
|
- col[i] = lanczos3_x(x, &row)
|
|
|
+ n := 6
|
|
|
+ kernel := func(x float32, j int) float32 {
|
|
|
+ return float32(Sinc(float64(x-float32(j-2)))) * float32(Sinc(float64((x-float32(j-2))/float32(3))))
|
|
|
}
|
|
|
-
|
|
|
- c := lanczos3_x(y, &col)
|
|
|
- return color.RGBA64{c[0], c[1], c[2], c[3]}
|
|
|
+ return filter(x, y, img, n, kernel)
|
|
|
}
|