Updated 2015-01-10 09:25:46 by dkf

ulis, 2003-12-07. A proc to blur an image.

David Easton, 2003-12-08. Speeded up 25%

(Original photo: [to fill])

## How it works edit

It works by adding some neighbor pixels:
```     0  1  2  3  4
.--.--.--.--.--.
0 |//|  |  |  |//|
.--.--.--.--.--.
1 |  |  |//|  |  |
.--.--.--.--.--.
2 |  |//|XX|//|  |
.--.--.--.--.--.
3 |  |  |//|  |  |
.--.--.--.--.--.
4 |//|  |  |  |//|
.--.--.--.--.--.```

The color of the central pixel is computed from all marked pixels:
```  p22 = (1 - coef) * p22
+ coef/8 * (p00 + p04 + p12 + p21 + p23 + p32 + p40 + p44)```

## The proc edit

```  namespace eval ::blur \
{
namespace export blur

package require Tk

proc blur {image coef} \
{
# check coef
if {\$coef < 0.0 || \$coef > 1.0} \
{ error "bad coef \"\$coef\": should be in the range 0.0, 1.0" }
if {\$coef < 1.e-5} { return \$image }
set coef2 [expr {\$coef / 8.0}]
# get the old image content
set width [image width \$image]
set height [image height \$image]
if {\$width * \$height == 0} { error "bad image" }
# create corresponding planes
for {set y 0} {\$y < \$height} {incr y} \
{
set r:row {}
set g:row {}
set b:row {}
for {set x 0} {\$x < \$width} {incr x} \
{
foreach {r g b} [\$image get \$x \$y] break
foreach c {r g b} { lappend \$c:row [set \$c] }
}
foreach c {r g b} { lappend \$c:data [set \$c:row] }
}
# blurring
for {set y 0} {\$y < \$height} {incr y} \
{
set row2 {}
for {set x 0} {\$x < \$width} {incr x} \
{
foreach c {r g b} \
{
set c00 [lindex [set \$c:data] [expr {\$y - 2}] [expr {\$x - 2}]]
set c01 [lindex [set \$c:data] [expr {\$y - 1}] [expr {\$x - 0}]]
set c02 [lindex [set \$c:data] [expr {\$y - 2}] [expr {\$x + 2}]]
set c10 [lindex [set \$c:data] [expr {\$y + 0}] [expr {\$x - 1}]]
set c11 [lindex [set \$c:data] [expr {\$y + 0}] [expr {\$x - 0}]]
set c12 [lindex [set \$c:data] [expr {\$y + 0}] [expr {\$x + 1}]]
set c20 [lindex [set \$c:data] [expr {\$y + 2}] [expr {\$x - 2}]]
set c21 [lindex [set \$c:data] [expr {\$y + 1}] [expr {\$x - 0}]]
set c22 [lindex [set \$c:data] [expr {\$y + 2}] [expr {\$x + 2}]]
foreach v {c00 c01 c02 c10 c12 c20 c21 c22} { if {[set \$v] == ""} { set \$v 0.0 } }
set cc [expr {int((1.0 - \$coef) * \$c11 + \$coef2 * (\$c00 + \$c01 + \$c02 + \$c10 + \$c12 + \$c20 + \$c21 + \$c22))}]
if {\$cc < 0} { set cc 0 }
if {\$cc > 255} { set cc 255 }
set \$c \$cc
}
lappend row2 [format #%02x%02x%02x \$r \$g \$b]
}
lappend data2 \$row2
}
# create the new image
set image2 [image create photo]
# fill the new image
\$image2 put \$data2
# return the new image
return \$image2
}

}```

## The demo edit

```  # to download the image:

package require Img
image create photo Photo -file image3.png
namespace import ::blur::blur
wm withdraw .
set n 0
foreach coef {0.0 0.5 1.0} \
{
set image [blur Photo \$coef]
toplevel .\$n
wm title .\$n "blur \$coef"
canvas .\$n.c -bd 0 -highlightt 0
.\$n.c create image 0 0 -anchor nw -image \$image
foreach {- - width height} [.\$n.c bbox all] break
.\$n.c config -width \$width -height \$height
pack .\$n.c
bind .\$n.c <Destroy> exit
update
incr n
}```

## Minor alterations edit

The code above...
`     foreach v {c00 c01 c02 c10 c12 c20 c21 c22} { if {[set \$v] == ""} { set \$v 0.0 } }`

checks to see if any of the pixels are blank, and sets them to black. This has an overall darkening effect on the image. Corrected it is:
```     foreach v {c00 c01 c02 c10 c12 c20 c21 c22} {
if {[set \$v] == ""} { set \$v [lindex [set \$c:data] \$y \$x] }
}```

I also speed this process up significantly (approximately 40% speed increase)
```        proc Blur { data coef } {
if {\$coef < 0.0 || \$coef > 1.0} { error "bad coef \"\$coef\": should be in the range 0.0, 1.0" }
if {\$coef < 1.e-5} { return \$image }
set coef2 [expr {\$coef / 8.0}]
if {[catch {set width [image width \$data]} blah ]} {return 0;}
set height [image height \$data]
for {set y 0} {\$y < \$height} {incr y} {
set r:row {}; set g:row {}; set b:row {};
for {set x 0} {\$x < \$width} {incr x} {
foreach {r g b} [\$data get \$x \$y] break
foreach c {r g b} {lappend \$c:row [set \$c];}
}
foreach c {r g b} {lappend \$c:data [set \$c:row];}
set row {}
for {set x 0} {\$x < \$width} {incr x} {
foreach c {r g b} {
set c00 [lindex [set \$c:data] [expr {\$y - 2}] [expr {\$x - 2}]]
set c01 [lindex [set \$c:data] [expr {\$y - 1}] [expr {\$x - 0}]]
set c02 [lindex [set \$c:data] [expr {\$y - 2}] [expr {\$x + 2}]]
set c10 [lindex [set \$c:data] [expr {\$y + 0}] [expr {\$x - 1}]]
set c11 [lindex [set \$c:data] [expr {\$y + 0}] [expr {\$x - 0}]]
set c12 [lindex [set \$c:data] [expr {\$y + 0}] [expr {\$x + 1}]]
set c20 [lindex [set \$c:data] [expr {\$y + 2}] [expr {\$x - 2}]]
set c21 [lindex [set \$c:data] [expr {\$y + 1}] [expr {\$x - 0}]]
set c22 [lindex [set \$c:data] [expr {\$y + 2}] [expr {\$x + 2}]]
foreach v {c00 c01 c02 c10 c12 c20 c21 c22} {
if {[set \$v] == ""} { set \$v [lindex [set \$c:data] \$y \$x] }
}
set cc [expr {int((1.0 - \$coef) * \$c11 + \$coef2 * (\$c00 + \$c01 + \$c02 + \$c10 + \$c12 + \$c20 + \$c21 + \$c22))}]
if {\$cc < 0} { set cc 0 }
if {\$cc > 255} { set cc 255 }
set \$c \$cc
}
lappend row [format #%02x%02x%02x \$r \$g \$b]
}
lappend data2 \$row
}
set blurred [image create photo]
\$blurred put \$data2
return \$blurred
}```

* modified by: Barry Skidmore

### More performance

DKF: After playing around and knowing what's really expensive and what isn't, I get much better performance with this:
```proc Blur { image coef } {
if {\$coef < 0.0 || \$coef > 1.0} {
error "bad coef \"\$coef\": should be in the range 0.0, 1.0"
}
if {\$coef < 1.e-5} {
return \$image
}
set coef2 [expr {\$coef / 8.0}]
set coef1 [expr {1.0 - \$coef}]
if {[catch {
set width [image width \$image]
set height [image height \$image]
}]} {
return 0
}
set data {}
for {set y 0} {\$y < \$height} {incr y} {
set row {}
for {set x 0} {\$x < \$width} {incr x} {
set pixel [\$image get \$x \$y]
if {![llength \$row]} {
lappend row \$pixel \$pixel
}
lappend row \$pixel
}
lappend row \$pixel \$pixel
if {![llength \$data]} {
lappend data \$row \$row
}
lappend data \$row
}
lappend data \$row \$row
# blurring
for {set y0 0;set y1 1;set y2 2;set y3 3;set y4 4} {\$y0 < \$height} {incr y0;incr y1;incr y2;incr y3;incr y4} {
set row2 {}
for {set x0 0;set x1 1;set x2 2;set x3 3;set x4 4} {\$x0 < \$width} {incr x0;incr x1;incr x2;incr x3;incr x4} {
set cc [expr {
int(\$coef1 * ([lindex \$data \$y2 \$x2 0]) +
\$coef2 * ([lindex \$data \$y0 \$x0 0] +
[lindex \$data \$y1 \$x2 0] +
[lindex \$data \$y0 \$x4 0] +
[lindex \$data \$y2 \$x1 0] +
[lindex \$data \$y2 \$x3 0] +
[lindex \$data \$y4 \$x0 0] +
[lindex \$data \$y3 \$x2 0] +
[lindex \$data \$y4 \$x4 0]))
}]
set r [expr {\$cc<0?0:\$cc>255?255:\$cc}]
set cc [expr {
int(\$coef1 * ([lindex \$data \$y2 \$x2 1]) +
\$coef2 * ([lindex \$data \$y0 \$x0 1] +
[lindex \$data \$y1 \$x2 1] +
[lindex \$data \$y0 \$x4 1] +
[lindex \$data \$y2 \$x1 1] +
[lindex \$data \$y2 \$x3 1] +
[lindex \$data \$y4 \$x0 1] +
[lindex \$data \$y3 \$x2 1] +
[lindex \$data \$y4 \$x4 1]))
}]
set g [expr {\$cc<0?0:\$cc>255?255:\$cc}]
set cc [expr {
int(\$coef1 * ([lindex \$data \$y2 \$x2 2]) +
\$coef2 * ([lindex \$data \$y0 \$x0 2] +
[lindex \$data \$y1 \$x2 2] +
[lindex \$data \$y0 \$x4 2] +
[lindex \$data \$y2 \$x1 2] +
[lindex \$data \$y2 \$x3 2] +
[lindex \$data \$y4 \$x0 2] +
[lindex \$data \$y3 \$x2 2] +
[lindex \$data \$y4 \$x4 2]))
}]
set b [expr {\$cc<0?0:\$cc>255?255:\$cc}]
lappend row2 [format #%02x%02x%02x \$r \$g \$b]
}
lappend data2 \$row2
}
set blurred [image create photo]
\$blurred put \$data2
return \$blurred
}```

This goes at least twice as fast as the other versions.