Skip to content

Commit 5169081

Browse files
authored
Merge pull request #2 from julyskies/develop
Develop
2 parents 64e380b + dfda095 commit 5169081

File tree

5 files changed

+199
-2
lines changed

5 files changed

+199
-2
lines changed

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,12 @@ Full Fiber example is available at https://github.com/peterdee/filtering-backend
189189
rotated, format, processingError := brille.HueRotate(file, 278)
190190
```
191191

192+
- **Kuwahara filter**: an edge detection filter with dynamic aperture size. Requires aperture size to be provided, but due to the perfomance reasons maximum aperture size is limited to 40. This filter is very slow, and will probably be optimized in the future:
193+
194+
```golang
195+
kuwahara, format, processingError := brille.KuwaharaFilter(file, 9)
196+
```
197+
192198
- **Laplasian filter**: a static edge detection filter that uses a 3x3 kernel. It can be used to outline edges on an image:
193199

194200
```golang
@@ -211,10 +217,16 @@ Full Fiber example is available at https://github.com/peterdee/filtering-backend
211217
sepia, format, processingError := brille.Sepia(file)
212218
```
213219

220+
- **Sharpen filter**: image sharpening. Requires an ammount to be provided. Effect amount ranges from 0 to 100:
221+
222+
```golang
223+
sharpen, format, processingError := brille.SharpenFilter(file, 77)
224+
```
225+
214226
- **Sobel filter**: a static edge detection filter that uses a 3x3 kernel. It can be used to outline edges on an image:
215227

216228
```golang
217-
sobel, format, processingError := brille.Sobel(file)
229+
sobel, format, processingError := brille.SobelFilter(file)
218230
```
219231

220232
- **Solarize**: solarization affects image colors, partially inversing the colors. Requires a threshold to be provided. Threshold ranges from 0 to 255:
@@ -223,6 +235,10 @@ Full Fiber example is available at https://github.com/peterdee/filtering-backend
223235
solarized, format, processingError := brille.Solarize(file, 99)
224236
```
225237

238+
### Environment variables
239+
240+
- `BRILLE_JPEG_QUALITY` (`int`) - controls output quality for JPEG images, should be a number from 0 (low quality) to 100 (highest quality)
241+
226242
### License
227243

228244
[MIT](./LICENSE.md)

index.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,24 @@ func HueRotate(file io.Reader, angle int) (io.Reader, string, error) {
220220
return encoded, format, nil
221221
}
222222

223+
// aperture: 0 to 40
224+
func KuwaharaFilter(file io.Reader, aperture uint) (io.Reader, string, error) {
225+
if file == nil {
226+
return nil, "", errors.New(constants.ERROR_NO_FILE_PROVIDED)
227+
}
228+
aperture = utilities.MaxMin(aperture, 40, 0)
229+
source, format, preparationError := utilities.PrepareSource(file)
230+
if preparationError != nil {
231+
return nil, "", preparationError
232+
}
233+
kuwahara := processing.KuwaharaFilter(source, aperture)
234+
encoded, encodingError := utilities.PrepareResult(kuwahara, format)
235+
if encodingError != nil {
236+
return nil, "", encodingError
237+
}
238+
return encoded, format, nil
239+
}
240+
223241
func LaplasianFilter(file io.Reader) (io.Reader, string, error) {
224242
if file == nil {
225243
return nil, "", errors.New(constants.ERROR_NO_FILE_PROVIDED)
@@ -300,6 +318,24 @@ func Sepia(file io.Reader) (io.Reader, string, error) {
300318
return encoded, format, nil
301319
}
302320

321+
// amount: 0 to 100
322+
func SharpenFilter(file io.Reader, amount uint) (io.Reader, string, error) {
323+
if file == nil {
324+
return nil, "", errors.New(constants.ERROR_NO_FILE_PROVIDED)
325+
}
326+
mix := float64(utilities.MaxMin(amount, 100, 0)) / 100
327+
source, format, preparationError := utilities.PrepareSource(file)
328+
if preparationError != nil {
329+
return nil, "", preparationError
330+
}
331+
sharpen := processing.Sharpen(source, mix)
332+
encoded, encodingError := utilities.PrepareResult(sharpen, format)
333+
if encodingError != nil {
334+
return nil, "", encodingError
335+
}
336+
return encoded, format, nil
337+
}
338+
303339
func SobelFilter(file io.Reader) (io.Reader, string, error) {
304340
if file == nil {
305341
return nil, "", errors.New(constants.ERROR_NO_FILE_PROVIDED)

processing/kuwahara-filter.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package processing
2+
3+
import (
4+
"image/color"
5+
6+
"github.com/julyskies/brille/utilities"
7+
)
8+
9+
func getAperture(axisValue, axisMax, apertureMin, apertureMax int) (int, int) {
10+
start, end := 0, axisMax
11+
if axisValue+apertureMin > 0 {
12+
start = axisValue + apertureMin
13+
}
14+
if axisValue+apertureMax < axisMax {
15+
end = axisValue + apertureMax
16+
}
17+
return start, end
18+
}
19+
20+
func KuwaharaFilter(source [][]color.Color, aperture uint) [][]color.Color {
21+
width, height := len(source), len(source[0])
22+
destination := utilities.CreateGrid(width, height)
23+
apertureHalf := int(aperture / 2)
24+
apertureMinX := [4]int{-apertureHalf, 0, -apertureHalf, 0}
25+
apertureMaxX := [4]int{0, apertureHalf, 0, apertureHalf}
26+
apertureMinY := [4]int{-apertureHalf, -apertureHalf, 0, 0}
27+
apertureMaxY := [4]int{0, 0, apertureHalf, apertureHalf}
28+
for x := 0; x < width; x += 1 {
29+
for y := 0; y < height; y += 1 {
30+
pixelCount := [4]int{0, 0, 0, 0}
31+
rValues := [4]int{0, 0, 0, 0}
32+
gValues := [4]int{0, 0, 0, 0}
33+
bValues := [4]int{0, 0, 0, 0}
34+
maxRValue := [4]int{0, 0, 0, 0}
35+
maxGValue := [4]int{0, 0, 0, 0}
36+
maxBValue := [4]int{0, 0, 0, 0}
37+
minRValue := [4]int{255, 255, 255, 255}
38+
minGValue := [4]int{255, 255, 255, 255}
39+
minBValue := [4]int{255, 255, 255, 255}
40+
for i := 0; i < 4; i += 1 {
41+
x2start, x2end := getAperture(x, width, apertureMinX[i], apertureMaxX[i])
42+
y2start, y2end := getAperture(y, height, apertureMinY[i], apertureMaxY[i])
43+
for x2 := x2start; x2 < x2end; x2 += 1 {
44+
for y2 := y2start; y2 < y2end; y2 += 1 {
45+
r, g, b, _ := utilities.RGBA(source[x2][y2])
46+
rValues[i] += int(r)
47+
gValues[i] += int(g)
48+
bValues[i] += int(b)
49+
if int(r) > maxRValue[i] {
50+
maxRValue[i] = int(r)
51+
} else if int(r) < minRValue[i] {
52+
minRValue[i] = int(r)
53+
}
54+
if int(g) > maxGValue[i] {
55+
maxGValue[i] = int(g)
56+
} else if int(g) < minGValue[i] {
57+
minGValue[i] = int(g)
58+
}
59+
if int(b) > maxBValue[i] {
60+
maxBValue[i] = int(b)
61+
} else if int(b) < minBValue[i] {
62+
minBValue[i] = int(b)
63+
}
64+
pixelCount[i] += 1
65+
}
66+
}
67+
}
68+
j := 0
69+
MinDifference := 10000
70+
for i := 0; i < 4; i += 1 {
71+
cdR := maxRValue[i] - minRValue[i]
72+
cdG := maxGValue[i] - minGValue[i]
73+
cdB := maxBValue[i] - minBValue[i]
74+
CurrentDifference := cdR + cdG + cdB
75+
if CurrentDifference < MinDifference && pixelCount[i] > 0 {
76+
j = i
77+
MinDifference = CurrentDifference
78+
}
79+
}
80+
cR := uint8(rValues[j] / pixelCount[j])
81+
cG := uint8(gValues[j] / pixelCount[j])
82+
cB := uint8(bValues[j] / pixelCount[j])
83+
destination[x][y] = color.RGBA{cR, cG, cB, 255}
84+
}
85+
}
86+
return destination
87+
}

processing/sharpen.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package processing
2+
3+
import (
4+
"image/color"
5+
6+
"github.com/julyskies/brille/utilities"
7+
)
8+
9+
var sharpenKernel = [3][3]int{
10+
{-1, -1, -1},
11+
{-1, 9, -1},
12+
{-1, -1, -1},
13+
}
14+
15+
func Sharpen(source [][]color.Color, mix float64) [][]color.Color {
16+
width, height := len(source), len(source[0])
17+
destination := utilities.CreateGrid(width, height)
18+
for x := 0; x < width; x += 1 {
19+
for y := 0; y < height; y += 1 {
20+
sumR := 0
21+
sumG := 0
22+
sumB := 0
23+
for i := 0; i < 3; i += 1 {
24+
for j := 0; j < 3; j += 1 {
25+
k := utilities.GradientPoint(x, i, width)
26+
l := utilities.GradientPoint(y, j, height)
27+
r, g, b, _ := utilities.RGBA(source[x+k][y+l])
28+
sumR += int(r) * sharpenKernel[i][j]
29+
sumG += int(g) * sharpenKernel[i][j]
30+
sumB += int(b) * sharpenKernel[i][j]
31+
}
32+
}
33+
r, g, b, alpha := utilities.RGBA(source[x][y])
34+
R := utilities.MaxMin(float64(sumR)*mix+float64(r)*(1-mix), 255, 0)
35+
G := utilities.MaxMin(float64(sumG)*mix+float64(g)*(1-mix), 255, 0)
36+
B := utilities.MaxMin(float64(sumB)*mix+float64(b)*(1-mix), 255, 0)
37+
destination[x][y] = color.RGBA{uint8(R), uint8(G), uint8(B), alpha}
38+
}
39+
}
40+
return destination
41+
}

utilities/prepare-result.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"image/jpeg"
88
"image/png"
99
"io"
10+
"os"
11+
"strconv"
1012
)
1113

1214
func PrepareResult(result [][]color.Color, format string) (io.Reader, error) {
@@ -18,6 +20,15 @@ func PrepareResult(result [][]color.Color, format string) (io.Reader, error) {
1820
}
1921
}
2022

23+
jpegQualityENV := os.Getenv("BRILLE_JPEG_QUALITY")
24+
jpegQuality := 100
25+
if jpegQualityENV != "" {
26+
parsed, parsingError := strconv.Atoi(jpegQualityENV)
27+
if parsingError == nil {
28+
jpegQuality = MaxMin(parsed, 100, 0)
29+
}
30+
}
31+
2132
var buffer bytes.Buffer
2233
writer := io.Writer(&buffer)
2334
if format == "png" {
@@ -26,7 +37,13 @@ func PrepareResult(result [][]color.Color, format string) (io.Reader, error) {
2637
return nil, encodingError
2738
}
2839
} else {
29-
encodingError := jpeg.Encode(writer, nrgba.SubImage(nrgba.Rect), nil)
40+
encodingError := jpeg.Encode(
41+
writer,
42+
nrgba.SubImage(nrgba.Rect),
43+
&jpeg.Options{
44+
Quality: jpegQuality,
45+
},
46+
)
3047
if encodingError != nil {
3148
return nil, encodingError
3249
}

0 commit comments

Comments
 (0)