Skip to content

Commit 120e315

Browse files
committed
Optimize more linear filters
1 parent aaffd23 commit 120e315

9 files changed

+175
-106
lines changed

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ These filters are ready to be used and were optimized compared to previous imple
1212
- Box blur (dynamic)
1313
- Brightness
1414
- Contrast
15-
- Eight colors (color reduction filter)
15+
- Eight colors (color reduction filter) **(optimized, uses WaitGroup)**
1616
- Emboss filter (edge detection, static)
1717
- Flip image (horizontal, vertical)
1818
- Gamma correction
1919
- Gaussian blur (dynamic) **(optimized, uses WaitGroup)**
20-
- Grayscale (average, luminance)
20+
- Grayscale (average, luminance) **(optimized, uses WaitGroup)**
2121
- Hue rotate
22-
- Inversion
22+
- Inversion **(optimized, uses WaitGroup)**
2323
- Kuwahara filter (edge detection / coloring, dynamic)
2424
- Laplacian filter (edge detection, static)
2525
- Rotate image by fixed angle (90 / 180 / 270 degrees)

filters/eight-colors-optimized.go

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package filters
2+
3+
import (
4+
"math"
5+
"runtime"
6+
"sync"
7+
"time"
8+
)
9+
10+
type Color struct {
11+
R, G, B int
12+
}
13+
14+
var COLORS = [8]Color{
15+
{255, 0, 0},
16+
{0, 255, 0},
17+
{0, 0, 255},
18+
{255, 255, 0},
19+
{255, 0, 255},
20+
{0, 255, 255},
21+
{255, 255, 255},
22+
{0, 0, 0},
23+
}
24+
25+
func EightColors(path string) {
26+
img, format, openMS, convertMS := open(path)
27+
now := math.Round(float64(time.Now().UnixNano()) / 1000000)
28+
29+
pixLen := len(img.Pix)
30+
threads := runtime.NumCPU()
31+
pixPerThread := getPixPerThread(pixLen, threads)
32+
33+
var wg sync.WaitGroup
34+
35+
processing := func(thread int) {
36+
defer wg.Done()
37+
startIndex := pixPerThread * thread
38+
endIndex := clampMax(startIndex+pixPerThread, pixLen)
39+
for i := startIndex; i < endIndex; i += 4 {
40+
minDelta := 195076
41+
var selectedColor Color
42+
for j := range COLORS {
43+
indexColor := COLORS[j]
44+
rDifference := int(img.Pix[i]) - indexColor.R
45+
gDifference := int(img.Pix[i+1]) - indexColor.G
46+
bDifference := int(img.Pix[i+2]) - indexColor.B
47+
delta := rDifference*rDifference + gDifference*gDifference + bDifference*bDifference
48+
if delta < minDelta {
49+
minDelta = delta
50+
selectedColor = indexColor
51+
}
52+
}
53+
img.Pix[i] = uint8(selectedColor.R)
54+
img.Pix[i+1] = uint8(selectedColor.G)
55+
img.Pix[i+2] = uint8(selectedColor.B)
56+
}
57+
}
58+
59+
for t := 0; t < threads; t += 1 {
60+
wg.Add(1)
61+
go processing(t)
62+
}
63+
64+
wg.Wait()
65+
66+
processMS := int(math.Round(float64(time.Now().UnixNano())/1000000) - now)
67+
saveMS := save(img, format)
68+
sum := openMS + convertMS + processMS + saveMS
69+
println("open", openMS, "convert", convertMS, "process", processMS, "save", saveMS, "sum", sum)
70+
}

filters/eight-colors.go

-48
This file was deleted.

filters/grayscale-optimized.go

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package filters
2+
3+
import (
4+
"math"
5+
"runtime"
6+
"sync"
7+
"time"
8+
9+
"go-image-processing/constants"
10+
)
11+
12+
func Grayscale(path, grayscaleType string) {
13+
if grayscaleType != constants.GRAYSCALE_TYPE_AVERAGE &&
14+
grayscaleType != constants.GRAYSCALE_TYPE_LUMINANCE {
15+
grayscaleType = constants.GRAYSCALE_TYPE_AVERAGE
16+
}
17+
img, format, openMS, convertMS := open(path)
18+
now := math.Round(float64(time.Now().UnixNano()) / 1000000)
19+
20+
pixLen := len(img.Pix)
21+
threads := runtime.NumCPU()
22+
pixPerThread := getPixPerThread(pixLen, threads)
23+
24+
var wg sync.WaitGroup
25+
26+
processing := func(thread int) {
27+
defer wg.Done()
28+
startIndex := pixPerThread * thread
29+
endIndex := clampMax(startIndex+pixPerThread, pixLen)
30+
for i := startIndex; i < endIndex; i += 4 {
31+
var channel uint8
32+
if grayscaleType == constants.GRAYSCALE_TYPE_AVERAGE {
33+
channel = uint8((int(img.Pix[i]) + int(img.Pix[i+1]) + int(img.Pix[i+2])) / 3)
34+
} else {
35+
channel = uint8(
36+
(float64(img.Pix[i])*0.21 + float64(img.Pix[i+1])*0.72 + float64(img.Pix[i+2])*0.07),
37+
)
38+
}
39+
img.Pix[i], img.Pix[i+1], img.Pix[i+2] = channel, channel, channel
40+
}
41+
}
42+
43+
for t := 0; t < threads; t += 1 {
44+
wg.Add(1)
45+
go processing(t)
46+
}
47+
48+
wg.Wait()
49+
50+
processMS := int(math.Round(float64(time.Now().UnixNano())/1000000) - now)
51+
saveMS := save(img, format)
52+
sum := openMS + convertMS + processMS + saveMS
53+
println("open", openMS, "convert", convertMS, "process", processMS, "save", saveMS, "sum", sum)
54+
}

filters/grayscale.go

-32
This file was deleted.

filters/invert-optimized.go

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package filters
2+
3+
import (
4+
"math"
5+
"runtime"
6+
"sync"
7+
"time"
8+
)
9+
10+
func Invert(path string) {
11+
img, format, openMS, convertMS := open(path)
12+
now := math.Round(float64(time.Now().UnixNano()) / 1000000)
13+
14+
pixLen := len(img.Pix)
15+
threads := runtime.NumCPU()
16+
pixPerThread := getPixPerThread(pixLen, threads)
17+
18+
var wg sync.WaitGroup
19+
20+
processing := func(thread int) {
21+
defer wg.Done()
22+
startIndex := pixPerThread * thread
23+
endIndex := clampMax(startIndex+pixPerThread, pixLen)
24+
for i := startIndex; i < endIndex; i += 4 {
25+
img.Pix[i] = 255 - img.Pix[i]
26+
img.Pix[i+1] = 255 - img.Pix[i+1]
27+
img.Pix[i+2] = 255 - img.Pix[i+2]
28+
}
29+
}
30+
31+
for t := 0; t < threads; t += 1 {
32+
wg.Add(1)
33+
go processing(t)
34+
}
35+
36+
wg.Wait()
37+
38+
processMS := int(math.Round(float64(time.Now().UnixNano())/1000000) - now)
39+
saveMS := save(img, format)
40+
sum := openMS + convertMS + processMS + saveMS
41+
println("open", openMS, "convert", convertMS, "process", processMS, "save", saveMS, "sum", sum)
42+
}

filters/invert.go

-20
This file was deleted.
File renamed without changes.

main.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package main
22

3-
import "go-image-processing/filters"
3+
import (
4+
"go-image-processing/constants"
5+
"go-image-processing/filters"
6+
)
47

58
var FORMAT string
69

@@ -37,13 +40,13 @@ func main() {
3740
// filters.Flip(path, constants.FLIP_TYPE_VERTICAL)
3841
// filters.GammaCorrection(path, 0.7)
3942
// filters.GaussianBlur(path, 5.2)
40-
// filters.Grayscale(path, constants.GRAYSCALE_TYPE_AVERAGE)
43+
filters.Grayscale(path, constants.GRAYSCALE_TYPE_LUMINANCE)
4144
// filters.HueRotate(path, 252)
4245
// filters.Invert(path)
4346
// filters.Kuwahara(path, 4)
4447
// filters.Laplacian(path)
4548
// filters.RotateFixed(path, constants.ROTATE_FIXED_90)
46-
filters.Sepia(path)
49+
// filters.Sepia(path)
4750
// filters.Sharpen(path, 92)
4851
// filters.Sobel(path)
4952
// filters.Solarize(path, 100)

0 commit comments

Comments
 (0)