From 9884534579f193028a0eee1cd7828438332257f5 Mon Sep 17 00:00:00 2001 From: jst Date: Tue, 28 Jan 2014 18:48:08 +0100 Subject: [PATCH] Cache kernel weights for each row. For each row the convolution kernel is evaluated at fixed positions around "u". By calculating these values once for each row, a huge speedup is achieved. --- filters.go | 39 ++++++++++++++++++++++++++++++--------- resize.go | 6 +++--- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/filters.go b/filters.go index 47fbd35..6d5bef7 100644 --- a/filters.go +++ b/filters.go @@ -49,23 +49,32 @@ type filterModel struct { // temporary used by Interpolate tempRow []colorArray + + kernelWeight []float32 + weightSum float32 } -func (f *filterModel) convolution1d(x float32, p []colorArray) (c colorArray) { - var k float32 - var sum float32 = 0 +func (f *filterModel) SetKernelWeights(u float32) { + uf := int(u) - len(f.tempRow)/2 + 1 + u -= float32(uf) + f.weightSum = 0 - for j := range p { - k = f.kernel((x - float32(j)) * f.factorInv) - sum += k + for j := range f.tempRow { + f.kernelWeight[j] = f.kernel((u - float32(j)) * f.factorInv) + f.weightSum += f.kernelWeight[j] + } +} + +func (f *filterModel) convolution1d(x float32) (c colorArray) { + for j := range f.tempRow { for i := range c { - c[i] += p[j][i] * k + c[i] += f.tempRow[j][i] * f.kernelWeight[j] } } // normalize values for i := range c { - c[i] = c[i] / sum + c[i] = c[i] / f.weightSum } return @@ -79,7 +88,7 @@ func (f *filterModel) Interpolate(u float32, y int) color.RGBA64 { f.at(uf+i, y, &f.tempRow[i]) } - c := f.convolution1d(u, f.tempRow) + c := f.convolution1d(u) return color.RGBA64{ clampToUint16(c[0]), clampToUint16(c[1]), @@ -99,36 +108,48 @@ func createFilter(img image.Image, factor float32, size int, kernel func(float32 kernel, 1. / factor, &genericConverter{img}, make([]colorArray, sizeX), + make([]float32, sizeX), + 0, } case *image.RGBA: f = &filterModel{ kernel, 1. / factor, &rgbaConverter{img.(*image.RGBA)}, make([]colorArray, sizeX), + make([]float32, sizeX), + 0, } case *image.RGBA64: f = &filterModel{ kernel, 1. / factor, &rgba64Converter{img.(*image.RGBA64)}, make([]colorArray, sizeX), + make([]float32, sizeX), + 0, } case *image.Gray: f = &filterModel{ kernel, 1. / factor, &grayConverter{img.(*image.Gray)}, make([]colorArray, sizeX), + make([]float32, sizeX), + 0, } case *image.Gray16: f = &filterModel{ kernel, 1. / factor, &gray16Converter{img.(*image.Gray16)}, make([]colorArray, sizeX), + make([]float32, sizeX), + 0, } case *image.YCbCr: f = &filterModel{ kernel, 1. / factor, &ycbcrConverter{img.(*image.YCbCr)}, make([]colorArray, sizeX), + make([]float32, sizeX), + 0, } } diff --git a/resize.go b/resize.go index ec3582d..5742f62 100644 --- a/resize.go +++ b/resize.go @@ -32,6 +32,7 @@ import ( // Filter can interpolate at points (x,y) type Filter interface { + SetKernelWeights(u float32) Interpolate(u float32, y int) color.RGBA64 } @@ -87,11 +88,10 @@ func resizeSlice(input image.Image, output *image.RGBA64, interp InterpolationFu var u float32 var color color.RGBA64 for y := slice.Min.Y; y < slice.Max.Y; y++ { + u = scale*(float32(y)+adjust) + offset + filter.SetKernelWeights(u) for x := slice.Min.X; x < slice.Max.X; x++ { - u = scale*(float32(y)+adjust) + offset - color = filter.Interpolate(u, x) - i := output.PixOffset(x, y) output.Pix[i+0] = uint8(color.R >> 8) output.Pix[i+1] = uint8(color.R)