Skip to content

Commit

Permalink
Merge pull request #10 from julyskies/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
peterdee authored Mar 21, 2023
2 parents e10c160 + 3c7cd5b commit 5c8d517
Show file tree
Hide file tree
Showing 13 changed files with 217 additions and 100 deletions.
2 changes: 2 additions & 0 deletions .github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@ Full Fiber example is available at https://github.com/peterdee/filtering-backend

- `BRILLE_JPEG_QUALITY` (`int`) - controls output quality for JPEG images, should be a number from 0 (low quality) to 100 (highest quality). Highest quality is used by default.

- `BRILLE_THREADS` (`int`) - controls the number of threads used when performing image processing. By default `runtime.NumCPU()` value is used, and provided number should be less or equal to that value.

### Contributing

Please check [contributing rules](CONTRIBUTING.md).
Expand Down
27 changes: 21 additions & 6 deletions filters/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package filters

import (
"io"
"sync"

"github.com/julyskies/brille/v2/utilities"
)
Expand All @@ -11,13 +12,27 @@ func Binary(file io.Reader, threshold uint8) (io.Reader, string, error) {
if convertationError != nil {
return nil, "", convertationError
}
for i := 0; i < len(img.Pix); i += 4 {
average := uint8((int(img.Pix[i]) + int(img.Pix[i+1]) + int(img.Pix[i+2])) / 3)
channel := uint8(255)
if average < threshold {
channel = 0
pixLen := len(img.Pix)
threads := utilities.GetThreads()
pixPerThread := utilities.GetPixPerThread(pixLen, threads)
var wg sync.WaitGroup
processing := func(thread int) {
defer wg.Done()
startIndex := pixPerThread * thread
endIndex := utilities.ClampMax(startIndex+pixPerThread, pixLen)
for i := startIndex; i < endIndex; i += 4 {
average := uint8((int(img.Pix[i]) + int(img.Pix[i+1]) + int(img.Pix[i+2])) / 3)
channel := uint8(255)
if average < threshold {
channel = 0
}
img.Pix[i], img.Pix[i+1], img.Pix[i+2] = channel, channel, channel
}
img.Pix[i], img.Pix[i+1], img.Pix[i+2] = channel, channel, channel
}
for t := 0; t < threads; t += 1 {
wg.Add(1)
go processing(t)
}
wg.Wait()
return utilities.EncodeResult(img, format)
}
49 changes: 34 additions & 15 deletions filters/box-blur.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package filters

import (
"io"
"sync"

"github.com/julyskies/brille/v2/utilities"
)
Expand All @@ -13,23 +14,41 @@ func BoxBlur(file io.Reader, radius uint) (io.Reader, string, error) {
}
radiusInt := int(radius)
width, height := img.Rect.Max.X, img.Rect.Max.Y
for i := 0; i < len(img.Pix); i += 4 {
x, y := utilities.GetCoordinates(i/4, width)
sumR, sumG, sumB, pixelCount := 0, 0, 0, 0
x2s, x2e := utilities.GetAperture(x, width, -radiusInt, radiusInt)
y2s, y2e := utilities.GetAperture(y, height, -radiusInt, radiusInt)
for x2 := x2s; x2 < x2e; x2 += 1 {
for y2 := y2s; y2 < y2e; y2 += 1 {
px := utilities.GetPixel(x2, y2, width)
sumR += int(img.Pix[px])
sumG += int(img.Pix[px+1])
sumB += int(img.Pix[px+2])
pixelCount += 1
pixLen := len(img.Pix)
threads := utilities.GetThreads()
pixPerThread := utilities.GetPixPerThread(pixLen, threads)
result := make([]uint8, pixLen)
var wg sync.WaitGroup
processing := func(thread int) {
defer wg.Done()
startIndex := pixPerThread * thread
endIndex := utilities.ClampMax(startIndex+pixPerThread, pixLen)
for i := startIndex; i < endIndex; i += 4 {
x, y := utilities.GetCoordinates(i/4, width)
dR, dG, dB := 0, 0, 0
pixelCount := 0
x2s, x2e := utilities.GetAperture(x, width, -radiusInt, radiusInt)
y2s, y2e := utilities.GetAperture(y, height, -radiusInt, radiusInt)
for x2 := x2s; x2 < x2e; x2 += 1 {
for y2 := y2s; y2 < y2e; y2 += 1 {
px := utilities.GetPixel(x2, y2, width)
dR += int(img.Pix[px])
dG += int(img.Pix[px+1])
dB += int(img.Pix[px+2])
pixelCount += 1
}
}
result[i] = uint8(dR / pixelCount)
result[i+1] = uint8(dG / pixelCount)
result[i+2] = uint8(dB / pixelCount)
result[i+3] = img.Pix[i+3]
}
img.Pix[i] = uint8(sumR / pixelCount)
img.Pix[i+1] = uint8(sumG / pixelCount)
img.Pix[i+2] = uint8(sumB / pixelCount)
}
for t := 0; t < threads; t += 1 {
wg.Add(1)
go processing(t)
}
wg.Wait()
img.Pix = result
return utilities.EncodeResult(img, format)
}
25 changes: 20 additions & 5 deletions filters/brightness.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package filters

import (
"io"
"sync"

"github.com/julyskies/brille/v2/utilities"
)
Expand All @@ -11,11 +12,25 @@ func Brightness(file io.Reader, amount int) (io.Reader, string, error) {
if convertationError != nil {
return nil, "", convertationError
}
amount = utilities.MaxMin(amount, 255, -255)
for i := 0; i < len(img.Pix); i += 4 {
img.Pix[i] = uint8(utilities.MaxMin(int(img.Pix[i])+amount, 255, 0))
img.Pix[i+1] = uint8(utilities.MaxMin(int(img.Pix[i+1])+amount, 255, 0))
img.Pix[i+2] = uint8(utilities.MaxMin(int(img.Pix[i+2])+amount, 255, 0))
amount = utilities.Clamp(amount, 255, -255)
pixLen := len(img.Pix)
threads := utilities.GetThreads()
pixPerThread := utilities.GetPixPerThread(pixLen, threads)
var wg sync.WaitGroup
processing := func(thread int) {
defer wg.Done()
startIndex := pixPerThread * thread
endIndex := utilities.ClampMax(startIndex+pixPerThread, pixLen)
for i := startIndex; i < endIndex; i += 4 {
img.Pix[i] = uint8(utilities.Clamp(int(img.Pix[i])+amount, 255, 0))
img.Pix[i+1] = uint8(utilities.Clamp(int(img.Pix[i+1])+amount, 255, 0))
img.Pix[i+2] = uint8(utilities.Clamp(int(img.Pix[i+2])+amount, 255, 0))
}
}
for t := 0; t < threads; t += 1 {
wg.Add(1)
go processing(t)
}
wg.Wait()
return utilities.EncodeResult(img, format)
}
25 changes: 20 additions & 5 deletions filters/contrast.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package filters

import (
"io"
"sync"

"github.com/julyskies/brille/v2/utilities"
)
Expand All @@ -11,12 +12,26 @@ func Contrast(file io.Reader, amount int) (io.Reader, string, error) {
if convertationError != nil {
return nil, "", convertationError
}
amount = utilities.MaxMin(amount, 255, -255)
amount = utilities.Clamp(amount, 255, -255)
factor := float64(259*(amount+255)) / float64(255*(259-amount))
for i := 0; i < len(img.Pix); i += 4 {
img.Pix[i] = uint8(utilities.MaxMin(factor*(float64(img.Pix[i])-128)+128, 255, 0))
img.Pix[i+1] = uint8(utilities.MaxMin(factor*(float64(img.Pix[i+1])-128)+128, 255, 0))
img.Pix[i+2] = uint8(utilities.MaxMin(factor*(float64(img.Pix[i+2])-128)+128, 255, 0))
pixLen := len(img.Pix)
threads := utilities.GetThreads()
pixPerThread := utilities.GetPixPerThread(pixLen, threads)
var wg sync.WaitGroup
processing := func(thread int) {
defer wg.Done()
startIndex := pixPerThread * thread
endIndex := utilities.ClampMax(startIndex+pixPerThread, pixLen)
for i := startIndex; i < endIndex; i += 4 {
img.Pix[i] = uint8(utilities.Clamp(factor*(float64(img.Pix[i])-128)+128, 255, 0))
img.Pix[i+1] = uint8(utilities.Clamp(factor*(float64(img.Pix[i+1])-128)+128, 255, 0))
img.Pix[i+2] = uint8(utilities.Clamp(factor*(float64(img.Pix[i+2])-128)+128, 255, 0))
}
}
for t := 0; t < threads; t += 1 {
wg.Add(1)
go processing(t)
}
wg.Wait()
return utilities.EncodeResult(img, format)
}
45 changes: 30 additions & 15 deletions filters/eight-colors.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package filters

import (
"io"
"sync"

"github.com/julyskies/brille/v2/utilities"
)
Expand All @@ -26,23 +27,37 @@ func EightColors(file io.Reader) (io.Reader, string, error) {
if convertationError != nil {
return nil, "", convertationError
}
for i := 0; i < len(img.Pix); i += 4 {
minDelta := 195076
var selectedColor Color
for j := range COLORS {
indexColor := COLORS[j]
rDifference := int(img.Pix[i]) - indexColor.R
gDifference := int(img.Pix[i+1]) - indexColor.G
bDifference := int(img.Pix[i+2]) - indexColor.B
delta := rDifference*rDifference + gDifference*gDifference + bDifference*bDifference
if delta < minDelta {
minDelta = delta
selectedColor = indexColor
pixLen := len(img.Pix)
threads := utilities.GetThreads()
pixPerThread := utilities.GetPixPerThread(pixLen, threads)
var wg sync.WaitGroup
processing := func(thread int) {
defer wg.Done()
startIndex := pixPerThread * thread
endIndex := utilities.ClampMax(startIndex+pixPerThread, pixLen)
for i := startIndex; i < endIndex; i += 4 {
minDelta := 195076
var selectedColor Color
for j := range COLORS {
indexColor := COLORS[j]
rDifference := int(img.Pix[i]) - indexColor.R
gDifference := int(img.Pix[i+1]) - indexColor.G
bDifference := int(img.Pix[i+2]) - indexColor.B
delta := rDifference*rDifference + gDifference*gDifference + bDifference*bDifference
if delta < minDelta {
minDelta = delta
selectedColor = indexColor
}
}
img.Pix[i] = uint8(selectedColor.R)
img.Pix[i+1] = uint8(selectedColor.G)
img.Pix[i+2] = uint8(selectedColor.B)
}
img.Pix[i] = uint8(selectedColor.R)
img.Pix[i+1] = uint8(selectedColor.G)
img.Pix[i+2] = uint8(selectedColor.B)
}
for t := 0; t < threads; t += 1 {
wg.Add(1)
go processing(t)
}
wg.Wait()
return utilities.EncodeResult(img, format)
}
55 changes: 36 additions & 19 deletions filters/emboss.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package filters
import (
"io"
"math"
"sync"

"github.com/julyskies/brille/v2/utilities"
)
Expand All @@ -25,27 +26,43 @@ func Emboss(file io.Reader) (io.Reader, string, error) {
return nil, "", convertationError
}
width, height := img.Rect.Max.X, img.Rect.Max.Y
for i := 0; i < len(img.Pix); i += 4 {
x, y := utilities.GetCoordinates(i/4, width)
gradientX, gradientY := 0, 0
for m := 0; m < 3; m += 1 {
for n := 0; n < 3; n += 1 {
k := utilities.GradientPoint(x, m, width)
l := utilities.GradientPoint(y, n, height)
px := utilities.GetPixel(x+k, y+l, width)
average := (int(img.Pix[px]) + int(img.Pix[px+1]) + int(img.Pix[px+2])) / 3
gradientX += average * embossHorizontal[m][n]
gradientY += average * embossVertical[m][n]
pixLen := len(img.Pix)
result := make([]uint8, pixLen)
threads := utilities.GetThreads()
pixPerThread := utilities.GetPixPerThread(pixLen, threads)
var wg sync.WaitGroup
processing := func(thread int) {
defer wg.Done()
startIndex := pixPerThread * thread
endIndex := utilities.ClampMax(startIndex+pixPerThread, pixLen)
for i := startIndex; i < endIndex; i += 4 {
x, y := utilities.GetCoordinates(i/4, width)
gradientX := 0
gradientY := 0
for m := 0; m < 3; m += 1 {
for n := 0; n < 3; n += 1 {
k := utilities.GradientPoint(x, m, width)
l := utilities.GradientPoint(y, n, height)
px := utilities.GetPixel(x+k, y+l, width)
average := (int(img.Pix[px]) + int(img.Pix[px+1]) + int(img.Pix[px+2])) / 3
gradientX += average * embossHorizontal[m][n]
gradientY += average * embossVertical[m][n]
}
}
channel := uint8(
255 - utilities.ClampMax(
math.Sqrt(float64(gradientX*gradientX+gradientY*gradientY)),
255,
),
)
result[i], result[i+1], result[i+2], result[i+3] = channel, channel, channel, img.Pix[i+3]
}
colorCode := uint8(
255 - utilities.MaxMin(
math.Sqrt(float64(gradientX*gradientX+gradientY*gradientY)),
255,
0,
),
)
img.Pix[i], img.Pix[i+1], img.Pix[i+2] = colorCode, colorCode, colorCode
}
for t := 0; t < threads; t += 1 {
wg.Add(1)
go processing(t)
}
wg.Wait()
img.Pix = result
return utilities.EncodeResult(img, format)
}
47 changes: 31 additions & 16 deletions filters/flip.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package filters

import (
"io"
"sync"

"github.com/julyskies/brille/v2/constants"
"github.com/julyskies/brille/v2/utilities"
Expand All @@ -24,23 +25,37 @@ func Flip(file io.Reader, direction string) (io.Reader, string, error) {
if height%2 != 0 {
heightCorrection = 1
}
for i := 0; i < len(img.Pix); i += 4 {
x, y := utilities.GetCoordinates(i/4, width)
var j int
skip := true
if direction == constants.FLIP_DIRECTION_HORIZONTAL && x < width/2+widthCorrection {
j = utilities.GetPixel(width-x-1, y, width)
skip = false
}
if direction == constants.FLIP_DIRECTION_VERTICAL && y < height/2+heightCorrection {
j = utilities.GetPixel(x, height-y-1, width)
skip = false
}
if !skip {
r, g, b := img.Pix[i], img.Pix[i+1], img.Pix[i+2]
img.Pix[i], img.Pix[i+1], img.Pix[i+2] = img.Pix[j], img.Pix[j+1], img.Pix[j+2]
img.Pix[j], img.Pix[j+1], img.Pix[j+2] = r, g, b
pixLen := len(img.Pix)
threads := utilities.GetThreads()
pixPerThread := utilities.GetPixPerThread(pixLen, threads)
var wg sync.WaitGroup
processing := func(thread int) {
defer wg.Done()
startIndex := pixPerThread * thread
endIndex := utilities.ClampMax(startIndex+pixPerThread, pixLen)
for i := startIndex; i < endIndex; i += 4 {
x, y := utilities.GetCoordinates(i/4, width)
var j int
skip := true
if direction == constants.FLIP_DIRECTION_HORIZONTAL && x < width/2+widthCorrection {
j = utilities.GetPixel(width-x-1, y, width)
skip = false
}
if direction == constants.FLIP_DIRECTION_VERTICAL && y < height/2+heightCorrection {
j = utilities.GetPixel(x, height-y-1, width)
skip = false
}
if !skip {
r, g, b := img.Pix[i], img.Pix[i+1], img.Pix[i+2]
img.Pix[i], img.Pix[i+1], img.Pix[i+2] = img.Pix[j], img.Pix[j+1], img.Pix[j+2]
img.Pix[j], img.Pix[j+1], img.Pix[j+2] = r, g, b
}
}
}
for t := 0; t < threads; t += 1 {
wg.Add(1)
go processing(t)
}
wg.Wait()
return utilities.EncodeResult(img, format)
}
Loading

0 comments on commit 5c8d517

Please sign in to comment.