[Image-SIG] Unsharp Masking?
Kevin@Cazabon.com
kevin@cazabon.com
Sun, 17 Mar 2002 16:18:12 -0700
This is a multi-part message in MIME format.
------=_NextPart_000_000F_01C1CDCF.55D6CF30
Content-Type: multipart/alternative;
boundary="----=_NextPart_001_0010_01C1CDCF.55D6CF30"
------=_NextPart_001_0010_01C1CDCF.55D6CF30
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
Does anyone have a FAST unsharp-masking method for use with PIL?
I wrote a brute-force module for this in Python today (attached, if it =
gets through the newsgroup filters) that works just fine, but it's =
mighty slow... anyone have something faster? The built in =
ImageEnhance.Sharpness filter is OK for very basic stuff, but the best =
way to sharpen is through an unsharp-mask type algorithm... it's much =
more powerful and controllable.
For those that don't know how unsharp masking works, here's the basics:
1) a copy of the image is blurred using a gaussian-type blurring =
algorighm (this is the 'unsharp' part), with a user-defined radius for =
the blur.
2) the blurred image is then compared (pixel by pixel if necessary) to =
the original image
3) the amount of difference between the blurred pixel and original =
pixel determines if it is an edge or not. If the difference is greater =
than the user-specified "threshold", sharpening is performed in step 4)
4) the pixel is changed by the OPPOSITE amount of the difference =
between the original and blurred pixels, multiplied by the user-defined =
"percentage"
Any help is appreciated in speeding this up!
Kevin Cazabon.
------=_NextPart_001_0010_01C1CDCF.55D6CF30
Content-Type: text/html;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=3DContent-Type content=3D"text/html; =
charset=3Diso-8859-1">
<META content=3D"MSHTML 5.50.4913.1100" name=3DGENERATOR>
<STYLE></STYLE>
</HEAD>
<BODY bgColor=3D#ffffff>
<DIV><FONT face=3DArial size=3D2>Does anyone have a FAST unsharp-masking =
method for=20
use with PIL?</FONT></DIV>
<DIV><FONT face=3DArial size=3D2></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2>I wrote a brute-force module for this =
in Python=20
today (attached, if it gets through the newsgroup =
filters) that works=20
just fine, but it's mighty slow... anyone have something faster? =
The built=20
in ImageEnhance.Sharpness filter is OK for very basic stuff, but the =
best way to=20
sharpen is through an unsharp-mask type algorithm... it's much more =
powerful=20
and controllable.</FONT></DIV>
<DIV><FONT face=3DArial size=3D2></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2>For those that don't know how unsharp =
masking=20
works, here's the basics:</FONT></DIV>
<DIV><FONT face=3DArial size=3D2></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2>1) a copy of the image is blurred =
using a=20
gaussian-type blurring algorighm (this is the 'unsharp' part), with a=20
user-defined radius for the blur.</FONT></DIV>
<DIV><FONT face=3DArial size=3D2></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2>2) the blurred image is then =
compared (pixel=20
by pixel if necessary) to the original image</FONT></DIV>
<DIV><FONT face=3DArial size=3D2></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2>3) the amount of difference =
between the=20
blurred pixel and original pixel determines if it is an edge or =
not. If=20
the difference is greater than the user-specified "threshold", =
sharpening is=20
performed in step 4)</FONT></DIV>
<DIV><FONT face=3DArial size=3D2></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2>4) the pixel is changed by the =
OPPOSITE=20
amount of the difference between the original and blurred pixels, =
multiplied by=20
the user-defined "percentage"</FONT></DIV>
<DIV><FONT face=3DArial size=3D2></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2>Any help is appreciated in speeding =
this=20
up!</FONT></DIV>
<DIV><FONT face=3DArial size=3D2></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2>Kevin =
Cazabon.</FONT></DIV></BODY></HTML>
------=_NextPart_001_0010_01C1CDCF.55D6CF30--
------=_NextPart_000_000F_01C1CDCF.55D6CF30
Content-Type: text/plain;
name="UnsharpMaskingModule.py"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="UnsharpMaskingModule.py"
# EditStation -- Unsharp Masking
# by Kevin Cazabon ( kevin@cazabon.com )
# Copyright 2002, all rights reserved
######################################################################
# Constants
######################################################################
######################################################################
# Imports
######################################################################
import Image
import array
######################################################################
# Functions
######################################################################
def unsharpMask (image, radius, weight, threshold):
# perform a gaussian blur on the image, using radius as a PERCENTAGE =
of the image size.
# this will allow realistic previewing on smaller versions of the =
same image.
# radius is the size of matrix to use for blurring
# weight is the percentage of how much of each out-lying pixel is =
taken into account,
# each, 'ring' is decreased by the same percentage.
#
# example: radius 5, weight 90 would result in a matrix like the =
following:
#
# 66 73 81 73 66=20
# 73 81 90 81 73=20
# 81 90 100 90 81=20
# 73 81 90 81 73=20
# 66 73 81 73 66
#
# then, use the difference between the orignal pixel and the new =
value to determine how much to increase
# the edge contrast.
radius =3D float(image.size[0] * image.size[1] * radius) / 300000.0
weight =3D float(weight)/100.0
imArray =3D array.array('B', image.tostring())
if image.mode =3D=3D "RGB":
channels =3D 3
elif image.mode =3D=3D "CMYK":
channels =3D 4
elif image.mode =3D=3D "L":
channels =3D 1
else: # cant process other mode images
return image
newImage =3D Image.new(image.mode, image.size)
for column in range(image.size[0]):
for row in range(image.size[1]):
newPixel =3D []
for c in range (channels):
totalValue =3D 0
origPixel =3D imArray[(column + (row * image.size[0])) * =
channels + c]
for xr in range(-int(radius/2), int(radius/2), 1):
for yr in range(-int(radius/2), int(radius/2), 1):
pixNumber =3D ((column + (row * image.size[0])) =
* channels) + c + (xr * channels) + (yr * image.size[0] * channels)
=20
if pixNumber < 0:
pixNumber =3D column * channels
elif pixNumber > len(imArray) - 1:
pixNumber =3D (column + (row * =
image.size[0])) * channels
=20
pixValue =3D imArray[pixNumber]
=20
pixWeight =3D pow(weight, (abs(xr) + abs(yr)))
totalValue =3D totalValue + ((origPixel - =
pixValue) * pixWeight)
newValue =3D int(totalValue / (radius*radius) + 0.5)
=20
if abs(origPixel - newValue) <=3D threshold:
newValue =3D origPixel
=20
newValue =3D origPixel + newValue
newPixel.append(newValue)
=20
newImage.putpixel((column, row), tuple(newPixel))
=20
return newImage
######################################################################
# Self Test
######################################################################
if __name__ =3D=3D "__main__":
im =3D Image.open("c:\\temp\\testsmall.tif")
im.show()
radius =3D 5
weight =3D 100
threshold =3D 3.0
=20
im =3D unsharpMask(im, radius, weight, threshold)
im.show()
------=_NextPart_000_000F_01C1CDCF.55D6CF30--