Skip to content

Commit aaffd23

Browse files
committed
Optimize more linear filters
1 parent b03703c commit aaffd23

File tree

9 files changed

+127
-80
lines changed

9 files changed

+127
-80
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ These filters are ready to be used and were optimized compared to previous imple
2323
- Kuwahara filter (edge detection / coloring, dynamic)
2424
- Laplacian filter (edge detection, static)
2525
- Rotate image by fixed angle (90 / 180 / 270 degrees)
26-
- Sepia
26+
- Sepia **(optimized, uses WaitGroup)**
2727
- Sharpen filter (dynamic)
2828
- Sobel filter (edge detection, static)
29-
- Solarize
29+
- Solarize **(optimized, uses WaitGroup)**
3030

3131
### In progress
3232

filters/binary-optimized.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ func Binary(path string, threshold uint8) {
1313

1414
pixLen := len(img.Pix)
1515
threads := runtime.NumCPU()
16-
pixPerThreadRaw := float64(pixLen) / float64(threads)
17-
pixPerThread := int(pixPerThreadRaw + (float64(threads) - math.Mod(pixPerThreadRaw, 4.0)))
16+
pixPerThread := getPixPerThread(pixLen, threads)
1817

1918
var wg sync.WaitGroup
2019

21-
processing := func(startIndex int) {
20+
processing := func(thread int) {
2221
defer wg.Done()
22+
startIndex := pixPerThread * thread
2323
endIndex := clampMax(startIndex+pixPerThread, pixLen)
2424
for i := startIndex; i < endIndex; i += 4 {
2525
average := uint8((int(img.Pix[i]) + int(img.Pix[i+1]) + int(img.Pix[i+2])) / 3)
@@ -33,7 +33,7 @@ func Binary(path string, threshold uint8) {
3333

3434
for t := 0; t < threads; t += 1 {
3535
wg.Add(1)
36-
go processing(pixPerThread * t)
36+
go processing(t)
3737
}
3838

3939
wg.Wait()

filters/sepia-optimized.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package filters
2+
3+
import (
4+
"math"
5+
"runtime"
6+
"sync"
7+
"time"
8+
)
9+
10+
func Sepia(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+
r, g, b := img.Pix[i], img.Pix[i+1], img.Pix[i+2]
26+
dR := clampMax(0.393*float64(r)+0.769*float64(g)+0.189*float64(b), 255.0)
27+
dG := clampMax(0.349*float64(r)+0.686*float64(g)+0.168*float64(b), 255.0)
28+
dB := clampMax(0.272*float64(r)+0.534*float64(g)+0.131*float64(b), 255.0)
29+
img.Pix[i], img.Pix[i+1], img.Pix[i+2] = uint8(dR), uint8(dG), uint8(dB)
30+
}
31+
}
32+
33+
for t := 0; t < threads; t += 1 {
34+
wg.Add(1)
35+
go processing(t)
36+
}
37+
38+
wg.Wait()
39+
40+
processMS := int(math.Round(float64(time.Now().UnixNano())/1000000) - now)
41+
saveMS := save(img, format)
42+
sum := openMS + convertMS + processMS + saveMS
43+
println("open", openMS, "convert", convertMS, "process", processMS, "save", saveMS, "sum", sum)
44+
}

filters/sepia.go

Lines changed: 0 additions & 24 deletions
This file was deleted.

filters/sobel.go

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,6 @@ var sobelVertical = [3][3]int{
1919
{-1, -2, -1},
2020
}
2121

22-
func getCoordinates(pixel, width int) (int, int) {
23-
return pixel % width, int(math.Floor(float64(pixel) / float64(width)))
24-
}
25-
26-
func getGradientPoint(axisValue, shift, axisLength int) int {
27-
if (axisValue + shift) >= axisLength {
28-
return axisLength - axisValue - 1
29-
}
30-
return shift
31-
}
32-
33-
func getPixel(x, y, width int) int {
34-
return ((y * width) + x) * 4
35-
}
36-
3722
func Sobel(path string) {
3823
img, format, openMS, convertMS := open(path)
3924
now := math.Round(float64(time.Now().UnixNano()) / 1000000)

filters/solarize-optimized.go

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

filters/solarize.go

Lines changed: 0 additions & 27 deletions
This file was deleted.

filters/utilities.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,26 @@ import (
1212
"time"
1313
)
1414

15+
func getCoordinates(pixel, width int) (int, int) {
16+
return pixel % width, int(math.Floor(float64(pixel) / float64(width)))
17+
}
18+
19+
func getGradientPoint(axisValue, shift, axisLength int) int {
20+
if (axisValue + shift) >= axisLength {
21+
return axisLength - axisValue - 1
22+
}
23+
return shift
24+
}
25+
26+
func getPixel(x, y, width int) int {
27+
return ((y * width) + x) * 4
28+
}
29+
30+
func getPixPerThread(pixLen, threads int) int {
31+
pixPerThreadRaw := float64(pixLen) / float64(threads)
32+
return int(pixPerThreadRaw + (float64(threads) - math.Mod(pixPerThreadRaw, 4.0)))
33+
}
34+
1535
func open(path string) (*image.RGBA, string, int, int) {
1636
now := math.Round(float64(time.Now().UnixNano()) / 1000000)
1737
file, err := os.Open(path)

main.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import "go-image-processing/filters"
55
var FORMAT string
66

77
func main() {
8-
path := "images/10.jpeg"
8+
path := "images/15.jpeg"
99
// img, f, openMS, convertMS := utilities.OpenFile(path)
1010
// FORMAT = f
1111
// now := math.Round(float64(time.Now().UnixNano()) / 1000000)
@@ -20,15 +20,15 @@ func main() {
2020
// saveMS := utilities.SaveFile("gaussEF-"+name, FORMAT, gaussEF)
2121
// saveMS := utilities.SaveFile("bilateral-"+name, FORMAT, bilateral)
2222

23-
// Done
23+
/* Alternative implementations */
24+
2425
// progress.BinaryCH(path, 122)
25-
// progress.GaussianBlurCH(path, blur)
26-
// progress.GaussianBlurCHSlow(path, blur)
26+
// progress.GaussianBlurCH(path, 10.0)
27+
// progress.GaussianBlurCHSlow(path, 10.0)
2728

2829
/* Optimized filters */
2930

30-
// filters.BilateralSlow(path, 3, 10, 15)
31-
filters.Binary(path, 122)
31+
// filters.Binary(path, 122)
3232
// filters.BoxBlur(path, 7)
3333
// filters.Brightness(path, 56)
3434
// filters.Contrast(path, 225)
@@ -43,10 +43,10 @@ func main() {
4343
// filters.Kuwahara(path, 4)
4444
// filters.Laplacian(path)
4545
// filters.RotateFixed(path, constants.ROTATE_FIXED_90)
46-
// filters.Sepia(path)
46+
filters.Sepia(path)
4747
// filters.Sharpen(path, 92)
4848
// filters.Sobel(path)
49-
// filters.Solarize(path, 175)
49+
// filters.Solarize(path, 100)
5050

5151
// sum := openMS + convertMS + processMS + saveMS
5252
// println("s open", openMS, "convert", convertMS, "process", processMS, "save", saveMS, "sum", sum)

0 commit comments

Comments
 (0)