Ver código fonte

filters.go simplified

jst 13 anos atrás
pai
commit
eaf9383af0
2 arquivos alterados com 77 adições e 111 exclusões
  1. 65 97
      filters.go
  2. 12 14
      resize.go

+ 65 - 97
filters.go

@@ -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.
-// 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))
 	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}
 	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)))
-		sum += kernel
+		k = kernel(x, j)
+		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
 }
 }
 
 
-// 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]}
 	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).
 // 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)
-
-	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)
 }
 }

+ 12 - 14
resize.go

@@ -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
-	// if #CPUs > width
-	n := 1
-	if NCPU < b.Dy() {
-		n = NCPU
-	} else {
-		n = b.Dy()
-	}
-
+	n := numJobs(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
+}