filters.go simplified

This commit is contained in:
jst 2012-09-14 23:12:05 +02:00
parent 524fd851ea
commit eaf9383af0
2 changed files with 85 additions and 119 deletions

View File

@ -19,6 +19,7 @@ package resize
import ( import (
"image" "image"
"image/color" "image/color"
"math"
) )
// color.RGBA64 as array // color.RGBA64 as array
@ -34,81 +35,24 @@ func clampToUint16(x float32) (y uint16) {
y = uint16(x) y = uint16(x)
if x < 0 { if x < 0 {
y = 0 y = 0
} else if x > float32(0xffff) { } else if x > float32(0xfffe) {
y = 0xffff y = 0xffff
} }
return return
} }
// Nearest-neighbor interpolation. func convolution1d(x float32, kernel func(float32, int) float32, p []RGBA) (c RGBA) {
// 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) {
x -= float32(int(x)) x -= float32(int(x))
var kernel float32 var k float32
var sum float32 = 0 // for kernel normalization var sum float32 = 0
l := [4]float32{0.0, 0.0, 0.0, 0.0} l := [4]float32{0.0, 0.0, 0.0, 0.0}
for j := range p { for j := range p {
kernel = float32(Sinc(float64(x-float32(j-1)))) * float32(Sinc(float64((x-float32(j-1))/2.0))) k = kernel(x, j)
sum += kernel sum += k
for i := range c { for i := range c {
l[i] += float32(p[j][i]) * kernel l[i] += float32(p[j][i]) * k
} }
} }
for i := range c { for i := range c {
@ -117,53 +61,77 @@ func lanczos2_x(x float32, p *[4]RGBA) (c RGBA) {
return return
} }
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
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 := convolution1d(y, kernel, col)
return color.RGBA64{c[0], c[1], c[2], c[3]}
}
// 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)
}
// 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)
}
// 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
}
return filter(x, y, img, n, kernel)
}
// Lanczos interpolation (a=2). // Lanczos interpolation (a=2).
func Lanczos2(x, y float32, img image.Image) color.RGBA64 { func Lanczos2(x, y float32, img image.Image) color.RGBA64 {
xf, yf := int(x), int(y) n := 4
kernel := func(x float32, j int) float32 {
var row [4]RGBA return float32(Sinc(float64(x-float32(j-1)))) * float32(Sinc(float64((x-float32(j-1))/float32(2))))
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)
} }
return filter(x, y, img, n, kernel)
c := lanczos2_x(y, &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))
var kernel float32
var sum float32 = 0 // for kernel normalization
l := [4]float32{0.0, 0.0, 0.0, 0.0}
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
}
}
for i := range c {
c[i] = clampToUint16(l[i] / sum)
}
return
} }
// Lanczos interpolation (a=3). // Lanczos interpolation (a=3).
func Lanczos3(x, y float32, img image.Image) color.RGBA64 { func Lanczos3(x, y float32, img image.Image) color.RGBA64 {
xf, yf := int(x), int(y) n := 6
kernel := func(x float32, j int) float32 {
var row [6]RGBA return float32(Sinc(float64(x-float32(j-2)))) * float32(Sinc(float64((x-float32(j-2))/float32(3))))
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)
} }
return filter(x, y, img, n, kernel)
c := lanczos3_x(y, &col)
return color.RGBA64{c[0], c[1], c[2], c[3]}
} }

View File

@ -30,11 +30,6 @@ import (
"runtime" "runtime"
) )
var (
// NCPU holds the number of available CPUs at runtime.
NCPU = runtime.NumCPU()
)
// Trans2 is a 2-dimensional linear transformation. // Trans2 is a 2-dimensional linear transformation.
type Trans2 [6]float32 type Trans2 [6]float32
@ -86,15 +81,7 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
resizedImg := image.NewRGBA64(image.Rect(0, 0, int(oldWidth/scaleX), int(oldHeight/scaleY))) resizedImg := image.NewRGBA64(image.Rect(0, 0, int(oldWidth/scaleX), int(oldHeight/scaleY)))
b := resizedImg.Bounds() b := resizedImg.Bounds()
// prevent resize from doing too much work n := numJobs(b.Dy())
// if #CPUs > width
n := 1
if NCPU < b.Dy() {
n = NCPU
} else {
n = b.Dy()
}
c := make(chan int, n) c := make(chan int, n)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
go func(b image.Rectangle, c chan int) { go func(b image.Rectangle, c chan int) {
@ -115,3 +102,14 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
return resizedImg return resizedImg
} }
// Set number of parallel jobs
// but prevent resize from doing too much work
// if #CPUs > width
func numJobs(d int) (n int) {
n = runtime.NumCPU()
if n > d {
n = d
}
return
}