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.
This commit is contained in:
jst 2014-01-28 18:48:08 +01:00
parent 8f586c4f06
commit 9884534579
2 changed files with 33 additions and 12 deletions

View File

@ -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,
}
}

View File

@ -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++ {
for x := slice.Min.X; x < slice.Max.X; x++ {
u = scale*(float32(y)+adjust) + offset
filter.SetKernelWeights(u)
for x := slice.Min.X; x < slice.Max.X; x++ {
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)