| package imaging |
| |
| import ( |
| "image" |
| "testing" |
| ) |
| |
| func TestConvolve3x3(t *testing.T) { |
| testCases := []struct { |
| name string |
| src image.Image |
| kernel [9]float64 |
| options *ConvolveOptions |
| want *image.NRGBA |
| }{ |
| { |
| "Convolve3x3 0x0", |
| &image.NRGBA{ |
| Rect: image.Rect(0, 0, 0, 0), |
| Stride: 0, |
| Pix: []uint8{}, |
| }, |
| [9]float64{ |
| 0, 0, 0, |
| 0, 1, 0, |
| 0, 0, 0, |
| }, |
| nil, |
| &image.NRGBA{Rect: image.Rect(0, 0, 0, 0)}, |
| }, |
| { |
| "Convolve3x3 4x4 identity", |
| &image.NRGBA{ |
| Rect: image.Rect(-1, -1, 3, 3), |
| Stride: 4 * 4, |
| Pix: []uint8{ |
| 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
| 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |
| 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, |
| 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, |
| }, |
| }, |
| [9]float64{ |
| 0, 0, 0, |
| 0, 1, 0, |
| 0, 0, 0, |
| }, |
| nil, |
| &image.NRGBA{ |
| Rect: image.Rect(0, 0, 4, 4), |
| Stride: 4 * 4, |
| Pix: []uint8{ |
| 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
| 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |
| 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, |
| 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, |
| }, |
| }, |
| }, |
| { |
| "Convolve3x3 4x4 abs", |
| &image.NRGBA{ |
| Rect: image.Rect(-1, -1, 3, 3), |
| Stride: 4 * 4, |
| Pix: []uint8{ |
| 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
| 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |
| 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, |
| 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, |
| }, |
| }, |
| [9]float64{ |
| 0, 0, 0, |
| 0, -1, 0, |
| 0, 0, 0, |
| }, |
| &ConvolveOptions{Abs: true}, |
| &image.NRGBA{ |
| Rect: image.Rect(0, 0, 4, 4), |
| Stride: 4 * 4, |
| Pix: []uint8{ |
| 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
| 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |
| 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, |
| 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, |
| }, |
| }, |
| }, |
| { |
| "Convolve3x3 4x4 bias", |
| &image.NRGBA{ |
| Rect: image.Rect(-1, -1, 3, 3), |
| Stride: 4 * 4, |
| Pix: []uint8{ |
| 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
| 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |
| 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, |
| 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, |
| }, |
| }, |
| [9]float64{ |
| 0, 0, 0, |
| 0, 1, 0, |
| 0, 0, 0, |
| }, |
| &ConvolveOptions{Bias: 0x10}, |
| &image.NRGBA{ |
| Rect: image.Rect(0, 0, 4, 4), |
| Stride: 4 * 4, |
| Pix: []uint8{ |
| 0x10, 0x11, 0x12, 0x03, 0x14, 0x15, 0x16, 0x07, 0x18, 0x19, 0x1a, 0x0b, 0x1c, 0x1d, 0x1e, 0x0f, |
| 0x20, 0x21, 0x22, 0x13, 0x24, 0x25, 0x26, 0x17, 0x28, 0x29, 0x2a, 0x1b, 0x2c, 0x2d, 0x2e, 0x1f, |
| 0x30, 0x31, 0x32, 0x23, 0x34, 0x35, 0x36, 0x27, 0x38, 0x39, 0x3a, 0x2b, 0x3c, 0x3d, 0x3e, 0x2f, |
| 0x40, 0x41, 0x42, 0x33, 0x44, 0x45, 0x46, 0x37, 0x48, 0x49, 0x4a, 0x3b, 0x4c, 0x4d, 0x4e, 0x3f, |
| }, |
| }, |
| }, |
| { |
| "Convolve3x3 4x4 norm", |
| &image.NRGBA{ |
| Rect: image.Rect(-1, -1, 3, 3), |
| Stride: 4 * 4, |
| Pix: []uint8{ |
| 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
| 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |
| 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, |
| 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, |
| }, |
| }, |
| [9]float64{ |
| 1, 1, 1, |
| 1, 1, 1, |
| 1, 1, 1, |
| }, |
| &ConvolveOptions{Normalize: true}, |
| &image.NRGBA{ |
| Rect: image.Rect(0, 0, 4, 4), |
| Stride: 4 * 4, |
| Pix: []uint8{ |
| 0x07, 0x08, 0x09, 0x03, 0x09, 0x0a, 0x0b, 0x07, 0x0d, 0x0e, 0x0f, 0x0b, 0x10, 0x11, 0x12, 0x0f, |
| 0x11, 0x12, 0x13, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1b, 0x1c, 0x1d, 0x1f, |
| 0x21, 0x22, 0x23, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c, 0x2d, 0x2f, |
| 0x2c, 0x2d, 0x2e, 0x33, 0x2f, 0x30, 0x31, 0x37, 0x33, 0x34, 0x35, 0x3b, 0x35, 0x36, 0x37, 0x3f, |
| }, |
| }, |
| }, |
| { |
| "Convolve3x3 3x3 laplacian", |
| &image.NRGBA{ |
| Rect: image.Rect(-1, -1, 2, 2), |
| Stride: 3 * 4, |
| Pix: []uint8{ |
| 0x00, 0x01, 0x01, 0xff, 0x00, 0x01, 0x02, 0xff, 0x00, 0x01, 0x03, 0xff, |
| 0x00, 0x01, 0x04, 0xff, 0x10, 0x10, 0x10, 0xff, 0x00, 0x01, 0x05, 0xff, |
| 0x00, 0x01, 0x06, 0xff, 0x00, 0x01, 0x07, 0xff, 0x00, 0x01, 0x08, 0xff, |
| }, |
| }, |
| [9]float64{ |
| -1, -1, -1, |
| -1, 8, -1, |
| -1, -1, -1, |
| }, |
| nil, |
| &image.NRGBA{ |
| Rect: image.Rect(0, 0, 3, 3), |
| Stride: 3 * 4, |
| Pix: []uint8{ |
| 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, |
| 0x00, 0x00, 0x00, 0xff, 0x80, 0x78, 0x5c, 0xff, 0x00, 0x00, 0x00, 0xff, |
| 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, |
| }, |
| }, |
| }, |
| } |
| |
| for _, tc := range testCases { |
| t.Run(tc.name, func(t *testing.T) { |
| got := Convolve3x3(tc.src, tc.kernel, tc.options) |
| if !compareNRGBA(got, tc.want, 0) { |
| t.Fatalf("got result %#v want %#v", got, tc.want) |
| } |
| }) |
| } |
| } |
| |
| func TestConvolve5x5(t *testing.T) { |
| testCases := []struct { |
| name string |
| src image.Image |
| kernel [25]float64 |
| options *ConvolveOptions |
| want *image.NRGBA |
| }{ |
| { |
| "Convolve5x5 4x4 translate", |
| &image.NRGBA{ |
| Rect: image.Rect(-1, -1, 3, 3), |
| Stride: 4 * 4, |
| Pix: []uint8{ |
| 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
| 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |
| 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, |
| 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, |
| }, |
| }, |
| [25]float64{ |
| 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 1, |
| }, |
| nil, |
| &image.NRGBA{ |
| Rect: image.Rect(0, 0, 4, 4), |
| Stride: 4 * 4, |
| Pix: []uint8{ |
| 0x28, 0x29, 0x2a, 0x03, 0x2c, 0x2d, 0x2e, 0x07, 0x2c, 0x2d, 0x2e, 0x0b, 0x2c, 0x2d, 0x2e, 0x0f, |
| 0x38, 0x39, 0x3a, 0x13, 0x3c, 0x3d, 0x3e, 0x17, 0x3c, 0x3d, 0x3e, 0x1b, 0x3c, 0x3d, 0x3e, 0x1f, |
| 0x38, 0x39, 0x3a, 0x23, 0x3c, 0x3d, 0x3e, 0x27, 0x3c, 0x3d, 0x3e, 0x2b, 0x3c, 0x3d, 0x3e, 0x2f, |
| 0x38, 0x39, 0x3a, 0x33, 0x3c, 0x3d, 0x3e, 0x37, 0x3c, 0x3d, 0x3e, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, |
| }, |
| }, |
| }, |
| } |
| |
| for _, tc := range testCases { |
| t.Run(tc.name, func(t *testing.T) { |
| got := Convolve5x5(tc.src, tc.kernel, tc.options) |
| if !compareNRGBA(got, tc.want, 0) { |
| t.Fatalf("got result %#v want %#v", got, tc.want) |
| } |
| }) |
| } |
| } |
| |
| func TestNormalizeKernel(t *testing.T) { |
| testCases := []struct { |
| name string |
| kernel []float64 |
| want []float64 |
| }{ |
| { |
| name: "positive sum", |
| kernel: []float64{ |
| 2, 0, 2, |
| 0, 2, 0, |
| 2, 0, 2, |
| }, |
| want: []float64{ |
| 0.2, 0, 0.2, |
| 0, 0.2, 0, |
| 0.2, 0, 0.2, |
| }, |
| }, |
| { |
| name: "negative sum", |
| kernel: []float64{ |
| -2, 0, -2, |
| 2, 2, 2, |
| -2, 0, -2, |
| }, |
| want: []float64{ |
| 1, 0, 1, |
| -1, -1, -1, |
| 1, 0, 1, |
| }, |
| }, |
| { |
| name: "zero sum", |
| kernel: []float64{ |
| 0, 2, 0, |
| 2, 0, -2, |
| 0, -2, 0, |
| }, |
| want: []float64{ |
| 0, 0.5, 0, |
| 0.5, 0, -0.5, |
| 0, -0.5, 0, |
| }, |
| }, |
| { |
| name: "all zero", |
| kernel: []float64{ |
| 0, 0, 0, |
| 0, 0, 0, |
| 0, 0, 0, |
| }, |
| want: []float64{ |
| 0, 0, 0, |
| 0, 0, 0, |
| 0, 0, 0, |
| }, |
| }, |
| } |
| for _, tc := range testCases { |
| t.Run(tc.name, func(t *testing.T) { |
| normalizeKernel(tc.kernel) |
| for i := range tc.kernel { |
| if tc.kernel[i] != tc.want[i] { |
| t.Fatalf("got kernel %v want %v", tc.kernel, tc.want) |
| } |
| } |
| }) |
| } |
| } |
| |
| func BenchmarkConvolve3x3(b *testing.B) { |
| b.ReportAllocs() |
| for i := 0; i < b.N; i++ { |
| Convolve3x3( |
| testdataBranchesJPG, |
| [9]float64{ |
| -1, -1, 0, |
| -1, 0, 1, |
| 0, 1, 1, |
| }, |
| nil, |
| ) |
| } |
| } |
| |
| func BenchmarkConvolve5x5(b *testing.B) { |
| b.ReportAllocs() |
| for i := 0; i < b.N; i++ { |
| Convolve5x5( |
| testdataBranchesJPG, |
| [25]float64{ |
| -1, -1, -1, -1, 0, |
| -1, -1, -1, 0, 1, |
| -1, -1, 0, 1, 1, |
| -1, 0, 1, 1, 1, |
| 0, 1, 1, 1, 1, |
| }, |
| nil, |
| ) |
| } |
| } |