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
|
// temporary used by Interpolate
|
||||||
tempRow []colorArray
|
tempRow []colorArray
|
||||||
|
|
||||||
|
kernelWeight []float32
|
||||||
|
weightSum float32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *filterModel) convolution1d(x float32, p []colorArray) (c colorArray) {
|
func (f *filterModel) SetKernelWeights(u float32) {
|
||||||
var k float32
|
uf := int(u) - len(f.tempRow)/2 + 1
|
||||||
var sum float32 = 0
|
u -= float32(uf)
|
||||||
|
f.weightSum = 0
|
||||||
|
|
||||||
for j := range p {
|
for j := range f.tempRow {
|
||||||
k = f.kernel((x - float32(j)) * f.factorInv)
|
f.kernelWeight[j] = f.kernel((u - float32(j)) * f.factorInv)
|
||||||
sum += k
|
f.weightSum += f.kernelWeight[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *filterModel) convolution1d(x float32) (c colorArray) {
|
||||||
|
for j := range f.tempRow {
|
||||||
for i := range c {
|
for i := range c {
|
||||||
c[i] += p[j][i] * k
|
c[i] += f.tempRow[j][i] * f.kernelWeight[j]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalize values
|
// normalize values
|
||||||
for i := range c {
|
for i := range c {
|
||||||
c[i] = c[i] / sum
|
c[i] = c[i] / f.weightSum
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -79,7 +88,7 @@ func (f *filterModel) Interpolate(u float32, y int) color.RGBA64 {
|
||||||
f.at(uf+i, y, &f.tempRow[i])
|
f.at(uf+i, y, &f.tempRow[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
c := f.convolution1d(u, f.tempRow)
|
c := f.convolution1d(u)
|
||||||
return color.RGBA64{
|
return color.RGBA64{
|
||||||
clampToUint16(c[0]),
|
clampToUint16(c[0]),
|
||||||
clampToUint16(c[1]),
|
clampToUint16(c[1]),
|
||||||
|
@ -99,36 +108,48 @@ func createFilter(img image.Image, factor float32, size int, kernel func(float32
|
||||||
kernel, 1. / factor,
|
kernel, 1. / factor,
|
||||||
&genericConverter{img},
|
&genericConverter{img},
|
||||||
make([]colorArray, sizeX),
|
make([]colorArray, sizeX),
|
||||||
|
make([]float32, sizeX),
|
||||||
|
0,
|
||||||
}
|
}
|
||||||
case *image.RGBA:
|
case *image.RGBA:
|
||||||
f = &filterModel{
|
f = &filterModel{
|
||||||
kernel, 1. / factor,
|
kernel, 1. / factor,
|
||||||
&rgbaConverter{img.(*image.RGBA)},
|
&rgbaConverter{img.(*image.RGBA)},
|
||||||
make([]colorArray, sizeX),
|
make([]colorArray, sizeX),
|
||||||
|
make([]float32, sizeX),
|
||||||
|
0,
|
||||||
}
|
}
|
||||||
case *image.RGBA64:
|
case *image.RGBA64:
|
||||||
f = &filterModel{
|
f = &filterModel{
|
||||||
kernel, 1. / factor,
|
kernel, 1. / factor,
|
||||||
&rgba64Converter{img.(*image.RGBA64)},
|
&rgba64Converter{img.(*image.RGBA64)},
|
||||||
make([]colorArray, sizeX),
|
make([]colorArray, sizeX),
|
||||||
|
make([]float32, sizeX),
|
||||||
|
0,
|
||||||
}
|
}
|
||||||
case *image.Gray:
|
case *image.Gray:
|
||||||
f = &filterModel{
|
f = &filterModel{
|
||||||
kernel, 1. / factor,
|
kernel, 1. / factor,
|
||||||
&grayConverter{img.(*image.Gray)},
|
&grayConverter{img.(*image.Gray)},
|
||||||
make([]colorArray, sizeX),
|
make([]colorArray, sizeX),
|
||||||
|
make([]float32, sizeX),
|
||||||
|
0,
|
||||||
}
|
}
|
||||||
case *image.Gray16:
|
case *image.Gray16:
|
||||||
f = &filterModel{
|
f = &filterModel{
|
||||||
kernel, 1. / factor,
|
kernel, 1. / factor,
|
||||||
&gray16Converter{img.(*image.Gray16)},
|
&gray16Converter{img.(*image.Gray16)},
|
||||||
make([]colorArray, sizeX),
|
make([]colorArray, sizeX),
|
||||||
|
make([]float32, sizeX),
|
||||||
|
0,
|
||||||
}
|
}
|
||||||
case *image.YCbCr:
|
case *image.YCbCr:
|
||||||
f = &filterModel{
|
f = &filterModel{
|
||||||
kernel, 1. / factor,
|
kernel, 1. / factor,
|
||||||
&ycbcrConverter{img.(*image.YCbCr)},
|
&ycbcrConverter{img.(*image.YCbCr)},
|
||||||
make([]colorArray, sizeX),
|
make([]colorArray, sizeX),
|
||||||
|
make([]float32, sizeX),
|
||||||
|
0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ import (
|
||||||
|
|
||||||
// Filter can interpolate at points (x,y)
|
// Filter can interpolate at points (x,y)
|
||||||
type Filter interface {
|
type Filter interface {
|
||||||
|
SetKernelWeights(u float32)
|
||||||
Interpolate(u float32, y int) color.RGBA64
|
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 u float32
|
||||||
var color color.RGBA64
|
var color color.RGBA64
|
||||||
for y := slice.Min.Y; y < slice.Max.Y; y++ {
|
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
|
u = scale*(float32(y)+adjust) + offset
|
||||||
|
filter.SetKernelWeights(u)
|
||||||
|
for x := slice.Min.X; x < slice.Max.X; x++ {
|
||||||
color = filter.Interpolate(u, x)
|
color = filter.Interpolate(u, x)
|
||||||
|
|
||||||
i := output.PixOffset(x, y)
|
i := output.PixOffset(x, y)
|
||||||
output.Pix[i+0] = uint8(color.R >> 8)
|
output.Pix[i+0] = uint8(color.R >> 8)
|
||||||
output.Pix[i+1] = uint8(color.R)
|
output.Pix[i+1] = uint8(color.R)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user