forked from viamrobotics/rdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconvolution.go
134 lines (123 loc) · 2.93 KB
/
convolution.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package rimage
import (
"image"
"image/color"
"math"
"gonum.org/v1/gonum/mat"
"go.viam.com/rdk/utils"
)
// GetSobelX returns the Kernel corresponding to the Sobel kernel in the x direction.
func GetSobelX() Kernel {
return Kernel{
[][]float64{
{-1, 0, 1},
{-2, 0, 2},
{-1, 0, 1},
},
3,
3,
}
}
// GetSobelY returns the Kernel corresponding to the Sobel kernel in the y direction.
func GetSobelY() Kernel {
return Kernel{
[][]float64{
{-1, -2, -1},
{0, 0, 0},
{1, 2, 1},
},
3,
3,
}
}
// GetBlur3 returns the Kernel corresponding to a mean averaging kernel.
func GetBlur3() Kernel {
return Kernel{
[][]float64{
{1, 1, 1},
{1, 1, 1},
{1, 1, 1},
},
3,
3,
}
}
// GetGaussian3 returns the Kernel corresponding to 3x3 Gaussian blurring kernel.
func GetGaussian3() Kernel {
return Kernel{
[][]float64{
{1, 2, 1},
{2, 4, 2},
{1, 2, 1},
},
3,
3,
}
}
// GetGaussian5 returns the Kernel corresponding to 5x5 Gaussian blurring kernel.
func GetGaussian5() Kernel {
return Kernel{
[][]float64{
{1, 4, 7, 4, 1},
{4, 16, 26, 16, 4},
{7, 26, 41, 26, 7},
{4, 16, 26, 16, 4},
{1, 4, 7, 4, 1},
},
5,
5,
}
}
// ConvolveGray applies a convolution matrix (Kernel) to a grayscale image.
// Example of usage:
//
// res, err := convolution.ConvolveGray(img, kernel, {1, 1}, BorderReflect)
//
// Note: the anchor represents a point inside the area of the kernel. After every step of the convolution the position
// specified by the anchor point gets updated on the result image.
func ConvolveGray(img *image.Gray, kernel *Kernel, anchor image.Point, border BorderPad) (*image.Gray, error) {
kernelSize := kernel.Size()
padded, err := PaddingGray(img, kernelSize, anchor, border)
if err != nil {
return nil, err
}
originalSize := img.Bounds().Size()
resultImage := image.NewGray(img.Bounds())
utils.ParallelForEachPixel(originalSize, func(x, y int) {
sum := float64(0)
for ky := 0; ky < kernelSize.Y; ky++ {
for kx := 0; kx < kernelSize.X; kx++ {
pixel := padded.GrayAt(x+kx, y+ky)
kE := kernel.At(kx, ky)
sum += float64(pixel.Y) * kE
}
}
sum = utils.Clamp(sum, 0, 255)
resultImage.Set(x, y, color.Gray{uint8(sum)})
})
return resultImage, nil
}
// ConvolveGrayFloat64 implements a gray float64 image convolution with the Kernel filter
// There is no clamping in this case.
func ConvolveGrayFloat64(m *mat.Dense, filter *Kernel) (*mat.Dense, error) {
h, w := m.Dims()
result := mat.NewDense(h, w, nil)
kernelSize := filter.Size()
padded, err := PaddingFloat64(m, kernelSize, image.Point{1, 1}, 0)
if err != nil {
return nil, err
}
utils.ParallelForEachPixel(image.Point{w, h}, func(x, y int) {
sum := float64(0)
for ky := 0; ky < kernelSize.Y; ky++ {
for kx := 0; kx < kernelSize.X; kx++ {
pixel := padded.At(y+ky, x+kx)
kE := filter.At(ky, kx)
sum += pixel * kE
}
}
sum = math.Floor(sum)
result.Set(y, x, sum)
})
return result, nil
}