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:
parent
8f586c4f06
commit
9884534579
39
filters.go
39
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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue
Block a user