Skip to content

Commit

Permalink
Merge pull request #4 from julyskies/develop
Browse files Browse the repository at this point in the history
v2.0.0
  • Loading branch information
peterdee authored Feb 8, 2023
2 parents d730b95 + 17871ac commit 7b8b4f0
Show file tree
Hide file tree
Showing 50 changed files with 879 additions and 1,120 deletions.
96 changes: 48 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ Minimal required Golang version: **v1.18**
go get github.com/julyskies/brille@latest
```

### Versioning

Brille `v2.0.X` have a number of breaking changes compared to `v1.0.X`:
- some of the filtering functions were removed
- some of the filtering functions were renamed
- some of the constants were renamed

Check `Available filters` section for more information regarding these changes.

### Usage

All of the Brille filter functions return 3 values:
Expand Down Expand Up @@ -43,7 +52,7 @@ func main() {
}
defer file.Close()

sobel, format, processingError := brille.SobelFilter(file)
sobel, format, processingError := brille.Sobel(file)
if processingError != nil {
log.Fatal(processingError)
}
Expand Down Expand Up @@ -94,7 +103,7 @@ func controller(context *fiber.Ctx) error {

result, format, processingError := brille.Grayscale(
fileHandle,
brille.GRAYSCALE_AVERAGE,
brille.GRAYSCALE_TYPE_LUMINANCE,
)
if processingError != nil {
return fiber.NewError(fiber.StatusInternalServerError)
Expand All @@ -109,106 +118,97 @@ Full Fiber example is available at https://github.com/peterdee/filtering-backend

### Available filters

- **Binary**: convert an image to 1 bit black and white. Requires a threshold value (from 0 to 255):
- **Binary**: converts an image to 1-bit black and white. Requires a threshold value (`uint8`, 0 to 255):

```golang
binary, format, processingError := brille.Binary(file, 155)
```

- **Box blur**: blur the image. Requires blur amount to be provided. Blur amount is a `uint` value. Box blur function will automatically reduce the amount value if it is too big, maximum amount is `min(width, height) / 2`:
- **Box blur**: blurs the image. Requires blur radius to be provided (`uint`, any value):

```golang
blurred, format, processingError := brille.BoxBlur(file, 5)
blurred, format, processingError := brille.BoxBlur(file, 7)
```

- **Brightness**: adjust image bightness. Requires brightness amount to be provided. Brightness amount ranges from -255 (darker) to 255 (brighter):
- **Brightness**: adjusts image bightness. Requires brightness amount to be provided (`int`, -255 to 255):

```golang
brightened, format, processingError := brille.Brightness(file, 107)
brightened, format, processingError := brille.Brightness(file, 75)
```

- **Color inversion**: invert image colors.
- **Color inversion**: inverts image colors:

```golang
inverted, format, processingError := brille.ColorInversion(file)
```

- **Contrast**: adjust image contrast. Requires contrast amount to be provided. Contrast amount ranges from -255 (less contrast) to 255 (more contrast):

```golang
contrastless, format, processingError := brille.Contrast(file, -40)
```

- **Eight colors**: this filter leaves only eight colors present on the image (red, green, blue, yellow, cyan, magenta, white, black).
- **Contrast**: adjusts image contrast. Requires contrast amount to be provided (`int`, -255 to 255):

```golang
indexedColors, format, processingError := brille.EightColors(file)
contrast, format, processingError := brille.Contrast(file, -45)
```

- **Emboss filter**: a static edge detection filter that uses a 3x3 kernel. It can be used to outline edges on an image:
- **Eight colors**: this filter leaves only eight colors present on the image (red, green, blue, yellow, cyan, magenta, white, black):

```golang
embossed, format, processingError := brille.EmbossFilter(file)
eightColors, format, processingError := brille.EightColors(file)
```

- **Flip horizontal**: flip image horizontally, basically reflect the image in *X* axis.
- **Emboss**: a static edge detection filter that uses a 3x3 kernel. It can be used to outline edges on an image:

```golang
flippedX, format, processingError := brille.FlipHorizontal(file)
embossed, format, processingError := brille.Emboss(file)
```

- **Flip vertical**: flip image vertically, basically reflect the image in *Y* axis.
- **Flip**: flips image horizontally or vertically. This filter requires a second argument - flip direction. Flip directions are available as `brille` module constants (FLIP_DIRECTION_HORIZONTAL is a horizontal mirroring and FLIP_DIRECTION_VERTICAL is vertical mirroring):

```golang
flippedY, format, processingError := brille.FlipVertical(file)
flipped, format, processingError := brille.Flip(
file,
brille.FLIP_DIRECTION_HORIZONTAL,
)
```

- **Gamma correction**: image gamma correction. Requires correction amount to be provided. Correction amount ranges from `0` to `3.99` (`float64`). By default image gamma equals to `1`, so providing a value less than `1` makes colors more intense, and values more than `1` decrease color intensity:
- **Gamma correction**: corrects image gamma. Requires correction amount to be provided (`float64`, 0 to 3.99). By default image gamma equals to 1, so providing a value less than that makes colors more intense, and larger values decrease color intensity:

```golang
corrected, format, processingError := brille.GammaCorrection(file, 2.05)
```

- **Grayscale**: turn colors into shades of gray. Requires grayscale type to be provided. There are 2 grayscale types available: `average` (or `mean`) and `luminocity` (or `weighted`). Both types are available as constants in `brille` module:
- **Grayscale**: turns colors into shades of gray. This filter requires a second argument - grayscale type. Grayscale types are available as `brille` module constants (GRAYSCALE_TYPE_AVERAGE and GRAYSCALE_TYPE_LUMINANCE):

```golang
grayAverage, format, processingError := brille.Grayscale(
file,
brille.GRAYSCALE_AVERAGE,
)

grayLuminocity, format, processingError := brille.Grayscale(
grayscale, format, processingError := brille.Grayscale(
file,
brille.GRAYSCALE_LUMINOCITY,
brille.GRAYSCALE_TYPE_LUMINANCE,
)
```

- **Hue rotation**: rotate image hue to change the colors. Requires an angle to be provided. Angle represents degree of a hue rotation, can be any `int` number:
- **Hue rotation**: rotates image hue. Requires an angle to be provided (`int`, any value):

```golang
rotated, format, processingError := brille.HueRotate(file, 278)
```

- **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:
- **Kuwahara**: an edge detection filter with dynamic radius. Requires radius to be provided (`uint`, any value). This filter is pretty slow, and will probably be optimized in the future:

```golang
kuwahara, format, processingError := brille.KuwaharaFilter(file, 9)
kuwahara, format, processingError := brille.Kuwahara(file, 5)
```

- **Laplasian filter**: a static edge detection filter that uses a 3x3 kernel. It can be used to outline edges on an image:
- **Laplacian**: a static edge detection filter that uses a 3x3 kernel. It can be used to outline edges on an image:

```golang
laplasian, format, processingError := brille.LaplasianFilter(file)
laplacian, format, processingError := brille.Laplacian(file)
```

- **Rotate image (fixed angle)**: rotate an image. Available fixed angeles are 90, 180 and 270 degrees (clockwise):
- **Rotate image (fixed angles)**: rotates an image clockwise (90, 180 or 270 degrees). This filter requires a second argument - rotation angle. Rotation angles are available as `brille` module constants (ROTATE_FIXED_90, ROTATE_FIXED_180 and ROTATE_FIXED_270):

```golang
rotated90, format, processingError := brille.Rotate90(file)

rotated180, format, processingError := brille.Rotate180(file)

rotated270, format, processingError := brille.Rotate270(file)
rotated270deg, format, processingError := brille.RotateFixed(
file,
brille.ROTATE_FIXED_270,
)
```

- **Sepia**: sepia color filter.
Expand All @@ -217,27 +217,27 @@ Full Fiber example is available at https://github.com/peterdee/filtering-backend
sepia, format, processingError := brille.Sepia(file)
```

- **Sharpen filter**: image sharpening. Requires an ammount to be provided. Effect amount ranges from 0 to 100:
- **Sharpen**: sharpens provided image. Requires an amount to be provided (`uint`, 0 to 100):

```golang
sharpen, format, processingError := brille.SharpenFilter(file, 77)
sharpen, format, processingError := brille.Sharpen(file, 50)
```

- **Sobel filter**: a static edge detection filter that uses a 3x3 kernel. It can be used to outline edges on an image:
- **Sobel**: a static edge detection filter that uses a 3x3 kernel. It can be used to outline edges on an image:

```golang
sobel, format, processingError := brille.SobelFilter(file)
sobel, format, processingError := brille.Sobel(file)
```

- **Solarize**: solarization affects image colors, partially inversing the colors. Requires a threshold to be provided. Threshold ranges from 0 to 255:
- **Solarize**: solarization affects image colors, partially inversing the colors. Requires a threshold to be provided (`uint8`, 0 to 255):

```golang
solarized, format, processingError := brille.Solarize(file, 99)
```

### Environment variables

- `BRILLE_JPEG_QUALITY` (`int`) - controls output quality for JPEG images, should be a number from 0 (low quality) to 100 (highest quality)
- `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.

### License

Expand Down
16 changes: 12 additions & 4 deletions constants/index.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
package constants

const ERROR_INVALID_GRAYSCALE_TYPE string = "invalid grayscale type"

const ERROR_NO_FILE_PROVIDED string = "no file provided"

const GRAYSCALE_AVERAGE string = "average"
const FLIP_DIRECTION_HORIZONTAL string = "horizontal"

const FLIP_DIRECTION_VERTICAL string = "vertical"

const GRAYSCALE_TYPE_AVERAGE string = "average"

const GRAYSCALE_TYPE_LUMINANCE string = "luminance"

const ROTATE_FIXED_90 uint = 90

const ROTATE_FIXED_180 uint = 180

const GRAYSCALE_LUMINOCITY string = "luminocity"
const ROTATE_FIXED_270 uint = 270
23 changes: 23 additions & 0 deletions filters/binary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package filters

import (
"io"

"github.com/julyskies/brille/utilities"
)

func Binary(file io.Reader, threshold uint8) (io.Reader, string, error) {
img, format, convertationError := utilities.DecodeSource(file)
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
}
img.Pix[i], img.Pix[i+1], img.Pix[i+2] = channel, channel, channel
}
return utilities.EncodeResult(img, format)
}
35 changes: 35 additions & 0 deletions filters/box-blur.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package filters

import (
"io"

"github.com/julyskies/brille/utilities"
)

func BoxBlur(file io.Reader, radius uint) (io.Reader, string, error) {
img, format, convertationError := utilities.DecodeSource(file)
if convertationError != nil {
return nil, "", convertationError
}
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
}
}
img.Pix[i] = uint8(sumR / pixelCount)
img.Pix[i+1] = uint8(sumG / pixelCount)
img.Pix[i+2] = uint8(sumB / pixelCount)
}
return utilities.EncodeResult(img, format)
}
21 changes: 21 additions & 0 deletions filters/brightness.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package filters

import (
"io"

"github.com/julyskies/brille/utilities"
)

func Brightness(file io.Reader, amount int) (io.Reader, string, error) {
img, format, convertationError := utilities.DecodeSource(file)
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))
}
return utilities.EncodeResult(img, format)
}
20 changes: 20 additions & 0 deletions filters/color-inversion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package filters

import (
"io"

"github.com/julyskies/brille/utilities"
)

func ColorInversion(file io.Reader) (io.Reader, string, error) {
img, format, convertationError := utilities.DecodeSource(file)
if convertationError != nil {
return nil, "", convertationError
}
for i := 0; i < len(img.Pix); i += 4 {
img.Pix[i] = 255 - img.Pix[i]
img.Pix[i+1] = 255 - img.Pix[i+1]
img.Pix[i+2] = 255 - img.Pix[i+2]
}
return utilities.EncodeResult(img, format)
}
22 changes: 22 additions & 0 deletions filters/contrast.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package filters

import (
"io"

"github.com/julyskies/brille/utilities"
)

func Contrast(file io.Reader, amount int) (io.Reader, string, error) {
img, format, convertationError := utilities.DecodeSource(file)
if convertationError != nil {
return nil, "", convertationError
}
amount = utilities.MaxMin(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))
}
return utilities.EncodeResult(img, format)
}
Loading

0 comments on commit 7b8b4f0

Please sign in to comment.