mirror of
https://github.com/esimov/caire.git
synced 2025-10-05 16:47:15 +08:00
Perf improvements on stackblur algorithm
This commit is contained in:
@@ -75,8 +75,6 @@ func (c *Carver) set(x, y int, px float64) {
|
|||||||
// - the minimum energy level is calculated by summing up the current pixel value
|
// - the minimum energy level is calculated by summing up the current pixel value
|
||||||
// with the minimum pixel value of the neighboring pixels from the previous row.
|
// with the minimum pixel value of the neighboring pixels from the previous row.
|
||||||
func (c *Carver) ComputeSeams(p *Processor, img *image.NRGBA) (*image.NRGBA, error) {
|
func (c *Carver) ComputeSeams(p *Processor, img *image.NRGBA) (*image.NRGBA, error) {
|
||||||
var srcImg *image.NRGBA
|
|
||||||
|
|
||||||
width, height := img.Bounds().Dx(), img.Bounds().Dy()
|
width, height := img.Bounds().Dx(), img.Bounds().Dy()
|
||||||
sobel = c.SobelDetector(img, float64(p.SobelThreshold))
|
sobel = c.SobelDetector(img, float64(p.SobelThreshold))
|
||||||
|
|
||||||
@@ -215,8 +213,13 @@ func (c *Carver) ComputeSeams(p *Processor, img *image.NRGBA) (*image.NRGBA, err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var srcImg *image.NRGBA
|
||||||
if p.BlurRadius > 0 {
|
if p.BlurRadius > 0 {
|
||||||
srcImg = c.StackBlur(sobel, uint32(p.BlurRadius))
|
srcImg = image.NewNRGBA(img.Bounds())
|
||||||
|
err := Stackblur(srcImg, sobel, uint32(p.BlurRadius))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error bluring the image: %w", err)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
srcImg = sobel
|
srcImg = sobel
|
||||||
}
|
}
|
||||||
|
@@ -318,7 +318,9 @@ func TestCarver_ShouldNotRemoveFaceZone(t *testing.T) {
|
|||||||
pixels := rgbToGrayscale(img)
|
pixels := rgbToGrayscale(img)
|
||||||
|
|
||||||
sobel := c.SobelDetector(img, float64(p.SobelThreshold))
|
sobel := c.SobelDetector(img, float64(p.SobelThreshold))
|
||||||
img = c.StackBlur(sobel, uint32(p.BlurRadius))
|
|
||||||
|
err = Stackblur(img, sobel, uint32(p.BlurRadius))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
cParams := pigo.CascadeParams{
|
cParams := pigo.CascadeParams{
|
||||||
MinSize: 100,
|
MinSize: 100,
|
||||||
|
9
go.mod
9
go.mod
@@ -6,9 +6,9 @@ require (
|
|||||||
gioui.org v0.8.0
|
gioui.org v0.8.0
|
||||||
github.com/disintegration/imaging v1.6.2
|
github.com/disintegration/imaging v1.6.2
|
||||||
github.com/esimov/pigo v1.4.5
|
github.com/esimov/pigo v1.4.5
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.10.0
|
||||||
golang.org/x/exp v0.0.0-20240707233637-46b078467d37
|
golang.org/x/exp v0.0.0-20240707233637-46b078467d37
|
||||||
golang.org/x/image v0.18.0
|
golang.org/x/image v0.23.0
|
||||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035
|
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,7 +18,8 @@ require (
|
|||||||
github.com/go-text/typesetting v0.2.1 // indirect
|
github.com/go-text/typesetting v0.2.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
golang.org/x/exp/shiny v0.0.0-20240707233637-46b078467d37 // indirect
|
golang.org/x/exp/shiny v0.0.0-20240707233637-46b078467d37 // indirect
|
||||||
golang.org/x/sys v0.22.0 // indirect
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
golang.org/x/text v0.16.0 // indirect
|
golang.org/x/text v0.21.0 // indirect
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
31
go.sum
31
go.sum
@@ -5,7 +5,6 @@ gioui.org v0.8.0/go.mod h1:vEMmpxMOd/iwJhXvGVIzWEbxMWhnMQ9aByOGQdlQ8rc=
|
|||||||
gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
|
gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
|
||||||
gioui.org/shader v1.0.8 h1:6ks0o/A+b0ne7RzEqRZK5f4Gboz2CfG+mVliciy6+qA=
|
gioui.org/shader v1.0.8 h1:6ks0o/A+b0ne7RzEqRZK5f4Gboz2CfG+mVliciy6+qA=
|
||||||
gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
|
gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||||
@@ -18,35 +17,35 @@ github.com/go-text/typesetting v0.2.1/go.mod h1:mTOxEwasOFpAMBjEQDhdWRckoLLeI/+q
|
|||||||
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0=
|
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0=
|
||||||
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
|
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||||
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w=
|
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w=
|
||||||
golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||||
golang.org/x/exp/shiny v0.0.0-20240707233637-46b078467d37 h1:SOSg7+sueresE4IbmmGM60GmlIys+zNX63d6/J4CMtU=
|
golang.org/x/exp/shiny v0.0.0-20240707233637-46b078467d37 h1:SOSg7+sueresE4IbmmGM60GmlIys+zNX63d6/J4CMtU=
|
||||||
golang.org/x/exp/shiny v0.0.0-20240707233637-46b078467d37/go.mod h1:3F+MieQB7dRYLTmnncoFbb1crS5lfQoTfDgQy6K4N0o=
|
golang.org/x/exp/shiny v0.0.0-20240707233637-46b078467d37/go.mod h1:3F+MieQB7dRYLTmnncoFbb1crS5lfQoTfDgQy6K4N0o=
|
||||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
|
||||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20191110171634-ad39bd3f0407/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20191110171634-ad39bd3f0407/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
|
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
|
||||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
185
stackblur.go
185
stackblur.go
@@ -1,16 +1,18 @@
|
|||||||
// Go implementation of StackBlur algorithm described here:
|
// Go implementation of the StackBlur algorithm
|
||||||
// http://incubator.quasimondo.com/processing/fast_blur_deluxe.php
|
// http://incubator.quasimondo.com/processing/fast_blur_deluxe.php
|
||||||
|
|
||||||
package caire
|
package caire
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"image"
|
"image"
|
||||||
|
"image/color"
|
||||||
)
|
)
|
||||||
|
|
||||||
// blurstack is a linked list containing the color value and a pointer to the next struct.
|
// blurStack is a linked list containing the color value and a pointer to the next struct.
|
||||||
type blurstack struct {
|
type blurStack struct {
|
||||||
r, g, b, a uint32
|
r, g, b, a uint32
|
||||||
next *blurstack
|
next *blurStack
|
||||||
}
|
}
|
||||||
|
|
||||||
var mulTable = []uint32{
|
var mulTable = []uint32{
|
||||||
@@ -51,11 +53,86 @@ var shgTable = []uint32{
|
|||||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||||
}
|
}
|
||||||
|
|
||||||
// StackBlur applies a blur filter to the provided image.
|
// Stackblur takes the source image and returns it's blurred version by applying the blur radius defined as parameter. The destination image must be a image.NRGBA.
|
||||||
// The radius defines the bluring average.
|
func Stackblur(dst, src image.Image, radius uint32) error {
|
||||||
func (c *Carver) StackBlur(img *image.NRGBA, radius uint32) *image.NRGBA {
|
// Limit the maximum blur radius to 255 to avoid overflowing the multable.
|
||||||
var stackEnd, stackIn, stackOut *blurstack
|
if int(radius) >= len(mulTable) {
|
||||||
var width, height = uint32(img.Bounds().Dx()), uint32(img.Bounds().Dy())
|
radius = uint32(len(mulTable) - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if radius < 1 {
|
||||||
|
return errors.New("blur radius must be greater than 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
img, ok := dst.(*image.NRGBA)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("the destination image must be image.NRGBA")
|
||||||
|
}
|
||||||
|
|
||||||
|
process(img, src, radius)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func process(dst *image.NRGBA, src image.Image, radius uint32) {
|
||||||
|
srcBounds := src.Bounds()
|
||||||
|
srcMinX := srcBounds.Min.X
|
||||||
|
srcMinY := srcBounds.Min.Y
|
||||||
|
|
||||||
|
dstBounds := srcBounds.Sub(srcBounds.Min)
|
||||||
|
dstW := dstBounds.Dx()
|
||||||
|
dstH := dstBounds.Dy()
|
||||||
|
|
||||||
|
switch src0 := src.(type) {
|
||||||
|
case *image.NRGBA:
|
||||||
|
rowSize := srcBounds.Dx() * 4
|
||||||
|
for dstY := 0; dstY < dstH; dstY++ {
|
||||||
|
di := src0.PixOffset(0, dstY)
|
||||||
|
si := src0.PixOffset(srcMinX, srcMinY+dstY)
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
copy(dst.Pix[di:di+rowSize], src0.Pix[si:si+rowSize])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *image.YCbCr:
|
||||||
|
for dstY := 0; dstY < dstH; dstY++ {
|
||||||
|
di := dst.PixOffset(0, dstY)
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
srcX := srcMinX + dstX
|
||||||
|
srcY := srcMinY + dstY
|
||||||
|
siy := src0.YOffset(srcX, srcY)
|
||||||
|
sic := src0.COffset(srcX, srcY)
|
||||||
|
r, g, b := color.YCbCrToRGB(src0.Y[siy], src0.Cb[sic], src0.Cr[sic])
|
||||||
|
dst.Pix[di+0] = r
|
||||||
|
dst.Pix[di+1] = g
|
||||||
|
dst.Pix[di+2] = b
|
||||||
|
dst.Pix[di+3] = 0xff
|
||||||
|
di += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
for dstY := 0; dstY < dstH; dstY++ {
|
||||||
|
di := dst.PixOffset(0, dstY)
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
c := color.NRGBAModel.Convert(src.At(srcMinX+dstX, srcMinY+dstY)).(color.NRGBA)
|
||||||
|
dst.Pix[di+0] = c.R
|
||||||
|
dst.Pix[di+1] = c.G
|
||||||
|
dst.Pix[di+2] = c.B
|
||||||
|
dst.Pix[di+3] = c.A
|
||||||
|
di += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blurImage(dst, radius)
|
||||||
|
}
|
||||||
|
|
||||||
|
func blurImage(src *image.NRGBA, radius uint32) {
|
||||||
|
var (
|
||||||
|
stackEnd *blurStack
|
||||||
|
stackIn *blurStack
|
||||||
|
stackOut *blurStack
|
||||||
|
)
|
||||||
|
|
||||||
|
var width, height = uint32(src.Bounds().Dx()), uint32(src.Bounds().Dy())
|
||||||
var (
|
var (
|
||||||
div, widthMinus1, heightMinus1, radiusPlus1, sumFactor uint32
|
div, widthMinus1, heightMinus1, radiusPlus1, sumFactor uint32
|
||||||
x, y, i, p, yp, yi, yw,
|
x, y, i, p, yp, yi, yw,
|
||||||
@@ -65,26 +142,17 @@ func (c *Carver) StackBlur(img *image.NRGBA, radius uint32) *image.NRGBA {
|
|||||||
pr, pg, pb, pa uint32
|
pr, pg, pb, pa uint32
|
||||||
)
|
)
|
||||||
|
|
||||||
// Limit the maximum blur radius to 255, otherwise it overflows the multable length
|
|
||||||
// and will panic with and index out of range error.
|
|
||||||
if int(radius) >= len(mulTable) {
|
|
||||||
radius = uint32(len(mulTable) - 1)
|
|
||||||
}
|
|
||||||
if radius < 1 {
|
|
||||||
radius = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
div = radius + radius + 1
|
div = radius + radius + 1
|
||||||
widthMinus1 = width - 1
|
widthMinus1 = width - 1
|
||||||
heightMinus1 = height - 1
|
heightMinus1 = height - 1
|
||||||
radiusPlus1 = radius + 1
|
radiusPlus1 = radius + 1
|
||||||
sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2
|
sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2
|
||||||
|
|
||||||
stackStart := new(blurstack)
|
stackStart := new(blurStack)
|
||||||
stack := stackStart
|
stack := stackStart
|
||||||
|
|
||||||
for i = 1; i < div; i++ {
|
for i = 1; i < div; i++ {
|
||||||
stack.next = new(blurstack)
|
stack.next = new(blurStack)
|
||||||
stack = stack.next
|
stack = stack.next
|
||||||
if i == radiusPlus1 {
|
if i == radiusPlus1 {
|
||||||
stackEnd = stack
|
stackEnd = stack
|
||||||
@@ -98,10 +166,10 @@ func (c *Carver) StackBlur(img *image.NRGBA, radius uint32) *image.NRGBA {
|
|||||||
for y = 0; y < height; y++ {
|
for y = 0; y < height; y++ {
|
||||||
rInSum, gInSum, bInSum, aInSum, rSum, gSum, bSum, aSum = 0, 0, 0, 0, 0, 0, 0, 0
|
rInSum, gInSum, bInSum, aInSum, rSum, gSum, bSum, aSum = 0, 0, 0, 0, 0, 0, 0, 0
|
||||||
|
|
||||||
pr = uint32(img.Pix[yi])
|
pr = uint32(src.Pix[yi])
|
||||||
pg = uint32(img.Pix[yi+1])
|
pg = uint32(src.Pix[yi+1])
|
||||||
pb = uint32(img.Pix[yi+2])
|
pb = uint32(src.Pix[yi+2])
|
||||||
pa = uint32(img.Pix[yi+3])
|
pa = uint32(src.Pix[yi+3])
|
||||||
|
|
||||||
rOutSum = radiusPlus1 * pr
|
rOutSum = radiusPlus1 * pr
|
||||||
gOutSum = radiusPlus1 * pg
|
gOutSum = radiusPlus1 * pg
|
||||||
@@ -131,10 +199,10 @@ func (c *Carver) StackBlur(img *image.NRGBA, radius uint32) *image.NRGBA {
|
|||||||
diff = i
|
diff = i
|
||||||
}
|
}
|
||||||
p = yi + (diff << 2)
|
p = yi + (diff << 2)
|
||||||
pr = uint32(img.Pix[p])
|
pr = uint32(src.Pix[p])
|
||||||
pg = uint32(img.Pix[p+1])
|
pg = uint32(src.Pix[p+1])
|
||||||
pb = uint32(img.Pix[p+2])
|
pb = uint32(src.Pix[p+2])
|
||||||
pa = uint32(img.Pix[p+3])
|
pa = uint32(src.Pix[p+3])
|
||||||
|
|
||||||
stack.r = pr
|
stack.r = pr
|
||||||
stack.g = pg
|
stack.g = pg
|
||||||
@@ -158,16 +226,16 @@ func (c *Carver) StackBlur(img *image.NRGBA, radius uint32) *image.NRGBA {
|
|||||||
|
|
||||||
for x = 0; x < width; x++ {
|
for x = 0; x < width; x++ {
|
||||||
pa = (aSum * mulSum) >> shgSum
|
pa = (aSum * mulSum) >> shgSum
|
||||||
img.Pix[yi+3] = uint8(pa)
|
src.Pix[yi+3] = uint8(pa)
|
||||||
|
|
||||||
if pa != 0 {
|
if pa != 0 {
|
||||||
img.Pix[yi] = uint8((rSum * mulSum) >> shgSum)
|
src.Pix[yi] = uint8((rSum * mulSum) >> shgSum)
|
||||||
img.Pix[yi+1] = uint8((gSum * mulSum) >> shgSum)
|
src.Pix[yi+1] = uint8((gSum * mulSum) >> shgSum)
|
||||||
img.Pix[yi+2] = uint8((bSum * mulSum) >> shgSum)
|
src.Pix[yi+2] = uint8((bSum * mulSum) >> shgSum)
|
||||||
} else {
|
} else {
|
||||||
img.Pix[yi] = 0
|
src.Pix[yi] = 0
|
||||||
img.Pix[yi+1] = 0
|
src.Pix[yi+1] = 0
|
||||||
img.Pix[yi+2] = 0
|
src.Pix[yi+2] = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
rSum -= rOutSum
|
rSum -= rOutSum
|
||||||
@@ -187,10 +255,10 @@ func (c *Carver) StackBlur(img *image.NRGBA, radius uint32) *image.NRGBA {
|
|||||||
}
|
}
|
||||||
p = (yw + p) << 2
|
p = (yw + p) << 2
|
||||||
|
|
||||||
stackIn.r = uint32(img.Pix[p])
|
stackIn.r = uint32(src.Pix[p])
|
||||||
stackIn.g = uint32(img.Pix[p+1])
|
stackIn.g = uint32(src.Pix[p+1])
|
||||||
stackIn.b = uint32(img.Pix[p+2])
|
stackIn.b = uint32(src.Pix[p+2])
|
||||||
stackIn.a = uint32(img.Pix[p+3])
|
stackIn.a = uint32(src.Pix[p+3])
|
||||||
|
|
||||||
rInSum += stackIn.r
|
rInSum += stackIn.r
|
||||||
gInSum += stackIn.g
|
gInSum += stackIn.g
|
||||||
@@ -230,10 +298,10 @@ func (c *Carver) StackBlur(img *image.NRGBA, radius uint32) *image.NRGBA {
|
|||||||
rInSum, gInSum, bInSum, aInSum, rSum, gSum, bSum, aSum = 0, 0, 0, 0, 0, 0, 0, 0
|
rInSum, gInSum, bInSum, aInSum, rSum, gSum, bSum, aSum = 0, 0, 0, 0, 0, 0, 0, 0
|
||||||
|
|
||||||
yi = x << 2
|
yi = x << 2
|
||||||
pr = uint32(img.Pix[yi])
|
pr = uint32(src.Pix[yi])
|
||||||
pg = uint32(img.Pix[yi+1])
|
pg = uint32(src.Pix[yi+1])
|
||||||
pb = uint32(img.Pix[yi+2])
|
pb = uint32(src.Pix[yi+2])
|
||||||
pa = uint32(img.Pix[yi+3])
|
pa = uint32(src.Pix[yi+3])
|
||||||
|
|
||||||
rOutSum = radiusPlus1 * pr
|
rOutSum = radiusPlus1 * pr
|
||||||
gOutSum = radiusPlus1 * pg
|
gOutSum = radiusPlus1 * pg
|
||||||
@@ -259,10 +327,10 @@ func (c *Carver) StackBlur(img *image.NRGBA, radius uint32) *image.NRGBA {
|
|||||||
|
|
||||||
for i = 1; i <= radius; i++ {
|
for i = 1; i <= radius; i++ {
|
||||||
yi = (yp + x) << 2
|
yi = (yp + x) << 2
|
||||||
pr = uint32(img.Pix[yi])
|
pr = uint32(src.Pix[yi])
|
||||||
pg = uint32(img.Pix[yi+1])
|
pg = uint32(src.Pix[yi+1])
|
||||||
pb = uint32(img.Pix[yi+2])
|
pb = uint32(src.Pix[yi+2])
|
||||||
pa = uint32(img.Pix[yi+3])
|
pa = uint32(src.Pix[yi+3])
|
||||||
|
|
||||||
stack.r = pr
|
stack.r = pr
|
||||||
stack.g = pg
|
stack.g = pg
|
||||||
@@ -293,16 +361,16 @@ func (c *Carver) StackBlur(img *image.NRGBA, radius uint32) *image.NRGBA {
|
|||||||
for y = 0; y < height; y++ {
|
for y = 0; y < height; y++ {
|
||||||
p = yi << 2
|
p = yi << 2
|
||||||
pa = (aSum * mulSum) >> shgSum
|
pa = (aSum * mulSum) >> shgSum
|
||||||
img.Pix[p+3] = uint8(pa)
|
src.Pix[p+3] = uint8(pa)
|
||||||
|
|
||||||
if pa > 0 {
|
if pa > 0 {
|
||||||
img.Pix[p] = uint8((rSum * mulSum) >> shgSum)
|
src.Pix[p] = uint8((rSum * mulSum) >> shgSum)
|
||||||
img.Pix[p+1] = uint8((gSum * mulSum) >> shgSum)
|
src.Pix[p+1] = uint8((gSum * mulSum) >> shgSum)
|
||||||
img.Pix[p+2] = uint8((bSum * mulSum) >> shgSum)
|
src.Pix[p+2] = uint8((bSum * mulSum) >> shgSum)
|
||||||
} else {
|
} else {
|
||||||
img.Pix[p] = 0
|
src.Pix[p] = 0
|
||||||
img.Pix[p+1] = 0
|
src.Pix[p+1] = 0
|
||||||
img.Pix[p+2] = 0
|
src.Pix[p+2] = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
rSum -= rOutSum
|
rSum -= rOutSum
|
||||||
@@ -322,10 +390,10 @@ func (c *Carver) StackBlur(img *image.NRGBA, radius uint32) *image.NRGBA {
|
|||||||
}
|
}
|
||||||
p = (x + (p * width)) << 2
|
p = (x + (p * width)) << 2
|
||||||
|
|
||||||
stackIn.r = uint32(img.Pix[p])
|
stackIn.r = uint32(src.Pix[p])
|
||||||
stackIn.g = uint32(img.Pix[p+1])
|
stackIn.g = uint32(src.Pix[p+1])
|
||||||
stackIn.b = uint32(img.Pix[p+2])
|
stackIn.b = uint32(src.Pix[p+2])
|
||||||
stackIn.a = uint32(img.Pix[p+3])
|
stackIn.a = uint32(src.Pix[p+3])
|
||||||
|
|
||||||
rInSum += stackIn.r
|
rInSum += stackIn.r
|
||||||
gInSum += stackIn.g
|
gInSum += stackIn.g
|
||||||
@@ -359,5 +427,4 @@ func (c *Carver) StackBlur(img *image.NRGBA, radius uint32) *image.NRGBA {
|
|||||||
yi += width
|
yi += width
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return img
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user