ImageMagick v6 Example

Removing Halos  

Download the scripts
link back arrow Physpics
link back arrow Annals of Zweibieren
link back arrow 2007
ImageMagick Examples Preface and Index
Introduction
Commands: nobg and nosur
Step-by-step How-To
Computing the Transparency Table
How-to with no Temporary Files
The Effect of Blur-Spread
The Complete Shell Scripts
bear with haloMouse me!
 
The bear at left has a "halo" of white pixels in the black background. Roll the mouse over it: no halo. In this case, the halo was removed only for black backgrounds, which is the best you can do with .gif images. This page shows how to banish the halo from any background, using .png images.


Introduction

Some pages, like this one, have background colors. When icons appear, it is better to show them with a transparent background, like star on transparent background, instead of in some fixed background, like star on white background. Extracting an icon or other image from a background is a common task, yet fraught with challenges. The background may not be a solid color, but several shades close to each other. With formats like JPEG, colors may intermix across the background/image boundary. For anti-aliasing, some pixels along the edge of the image may be a mixture of the image color and the background. Some pixels of the background color may get swept up along with the rest of the image. This latter is called a halo, and is illustrated above.

The general task can usually be stated as converting some particular color, the transparency color, in the original image so that all pixels of that color are made transparent. There may need to be some fuzz in interpreting the color so nearby hues are also made transparent. In the work here, the transparency color is always taken to be the color of the leftmost pixel in the top row of the image, sometimes denoted by p{0,0}.

The halo affects a small fraction of the pixels, but its removal is vital to a good looking result. When the original image has anti-aliased edges, some effort must be made to make the corresponding pixels partially transparent so the background shows through no matter its color. It is this effort that is the principle contribution of the technique reported here.

There are two cases. In the instances above the background surrounds the image. Pixels within the image that happen to be the transparency color should NOT be made transparent. In other instances, particularly text like "FRED", transparency should apply to all pixels of the transparency color so the background shows through the "holes" in the image. These two cases are covered by two separate scripts that implement the techniques being described:
  • nosur
- when transparency should surround the image
  • nobg
- when all background pixels should be transparent

On black backgrounds, the results below are not quite as good as the bear at right. For other backgrounds, the results are superior. The bears are .gif images (from ablewebs.com), while the techniques below rely on .png. Pixels in gif images can be either fully opaque or fully transparent, but not partially transparent. Png images can have different fractional transparency for each pixel. bear in santa claus hat from Able


Examples below show the command on the left and the image against two backgrounds on the right. To get bearOnWhite.bmp, I put the original haloed bear on a white background, and ran this command:

 nosur -f 20 bearOnWhite.bmp bear-nosur.png
The black outline of the bear can be thickened by using a smaller value for -f.
bear extracted from bear on white background
bear extracted from bear on white background
    
Here are three bear images—the same image across each row—on various color backgrounds.
Bear with "halo." The halo is light, so the bear looks okay on light backgrounds.
bear with halo bear with halo bear with halo bear with halo
Bear.gif adapted for a black background. Not so good on other backgrounds.
bear in santa claus hat from Able bear in santa claus hat from Able bear in santa claus hat from Able bear in santa claus hat from Able
Bear-nosur.png anti-aliased for any background with nosur.
bear extracted from bear on white background bear extracted from bear on white background bear extracted from bear on white background bear extracted from bear on white background
Note the imperfection of bear.png on a black background.For this background, hand tweaking would be needed, as it was with the original bear on a black background.


Download the scripts

The two scripts, nobg and nosur, have identical parameters and are invoked as follows, where brackets indicate optional elements:

    nobg  [-f fuzz-precent] [-b blur-spread] [-t]  file [outfile]
or
    nosur [-f fuzz-precent] [-b blur-spread] [-t]  file [outfile]

Both scripts convert file to outfile, while making transparent the background (nobg) or surroundings (nosur). If outfile is omitted, the output is written to the file
       fff-nobg.png  or  fff-nosur.png
where fff is the name file with its extension removed.

The optional switches are:
-f fuzz-percent
Percentage fuzz around transparency color.  All pixels having colors within this percent of the transparency color are also made transparent.  Default: 5
-b blur-spread
A parameter to the ImageMagick blur operator. This operation blurs the edge of the non-transparent areas so they partially transparent. Form: RxS, where R is the blur radius and S the standard deviation. Either value may be a decimal value like 0.5. R can be made high without too much blurring of the image edges. S over 3 will blur image edges. For examples see the section on the Blur-Spread. Default: 10x2 
-t
By default, images are trimmed to remove invisible outer rows or columns. The -t switch omits this step; so the image will remain its original size.


To test the algorithms I collected some random images. I made a comprehensive page of samples with various processing and selected a few to show here.

"FRED" as a jpg file
Text in PowerPoint
logo for cgoban, a go game monitor program
Logo for cgoban, the usual interface to the Kiseido go server
Ancient pot now in the Museum at Hangyang University
A photo I took in the Museum at HangYang University, Seoul
a caduseus with the scales of justice hanging, one on each side
Two combined cliparts
white puppy with red bow
Clipart

skull and crossbones
Once guarded a warning on the Toxic Well site.
puppy on open book -- yellow backgrounnd
Part of the logo for http://www.peteducation.com/
(yellow added)

Here are the best results for each image. They are unretouched, but I did adapt the script parameters to do as well as possible.

Almost a really good result. It is too bad the algorithm does not poke more deeply into little pockets.
 nobg FRED.jpg FRED-nobg.png
"FRED" as a jpg file -- transparent background "FRED" as a jpg file -- transparent background

As a more severe test, I overlaid FRED on a green background. The result shown here is the upper left corner at 4x lifesize. It retains green. On a red background, some transparency is visible, but not enough, especially in pockets.  
nobg -f 20 -b 10x3 \
GREDul.jpg GREDul-nobg.png
FRED on green processed with nobg -f 20 -b 10x3
FRED on green processed with nobg -f 20 -b 10x3
Without the blurring of -b 10x3, the green is even more apparent.

The default fuzz and blur wipe out too much of the delicate filagree. These smaller values work better.
 nobg -f 2 -b 5x.5 medlaw.png \
medlaw-nobg.png
a caduseus with the scales of justice hanging, one on each side -- transparent backgound a caduseus with the scales of justice hanging, one on each side -- transparent backgound
    
With a smaller fuzz we get a bit of a halo/ but this bigger fuzz makes the top a bit thiner. The smaller blur helps restore the thickness. 
 nosur -f 20 -b 2x.2 skull-crossbones.png \
skull-crossbones-nosur.png
skull and crossbones -- transparent surroundings skull and crossbones -- transparent surroundings
The nobg script does work, but its weird to have the background color inside the skull.

With nobg, big gobs of the middle of the pot are transparent, but nosur does fine. Note that there is a bit of halo at the very bottom, but that its partial transparency makes it seem almost natural. It could be removed with hand massaging of the pixels.

 nosur -f 10 KoreanVase.jpg 
KoreanVase-nosur.png
Ancient pot now in the Museum at Hangyang University -- transparent surroundings Ancient pot now in the Museum at Hangyang University -- transparent surroundings

The vanilla nobg leaves green halo blobs near the junctions of circle and square. Nosur leaves a shadow in the lower right. By increasing the fuzz, we get a really great result.
 nosur -f 20 -b 5x2 cgoban.jpg cgoban-nosur.png
logo for cgoban -- transparent surroundings logo for cgoban -- transparent surroundings

The fuzz factor had to be lowered for this image to avoid leaking background into the image. (But nobg did just fine.)
 nosur -f 1 petEduc.png petEduc-nosur.png
[IM Output] [IM Output]

For this puppy, nobg is the one that leaks background into the body. Attempts to remove the halo on the lower edge will also leak background into the pup's body.
 nosur pup-orig.jpg 
pup-nosur.png
white pup with red bow -- transparent surroundings [IM Output]



Step-by-step How-To

nobg -Make transparent all pixels of the background color

The basic approach is to see where the image differs from the transparency color and make those parts of the image transparent. To get anti-aliasing right, we blur the edges of the difference mask and use those values to generate partial transparency for edge pixels in the image.

1. Make a tile having the transparency color.
 convert petEduc.png +matte -crop 1x1+0+0 \
    +repage -scale 100x100 pet-tile.png
Rescaling to 100x100 may reduce later overhead. +matte removes the alpha channel from the image. Later we will use -matte to add one. The non-intuitive signs for these operations is a historical quirk.
[IM Output]

2. Create a version of the image tiled with the transparency color.
 convert petEduc.png -tile pet-tile.png \
    -draw 'color 0,0 reset' pet-bg.png
[IM Output]

3. Subtract the transparency color from the image. The result is an image where black means no difference and lighter colors are greater distance.
 convert petEduc.png pet-bg.png \
    -compose Difference -composite \
    pet-diff.png
[IM Output]

4. Threshold the image to pure black/white. The black pixels will be those whose color was within 5% of the transparency color.
 convert pet-diff.png +matte -colorspace Gray \
    -threshold 5% pet-mask.png
The -threshold argument is the fuzzpct.  Its default value is the 5%.
[IM Output]

5. (Start the magic) Blur the difference image just around the edges.
 convert pet-mask.png -blur 10x2 \
    pet-blurmask.png
[IM Output]

6. (Build the magic) To restrict the blur to the opaque part of the image, we  take the blurred mask and completely blacken it where the difference mask is black.
 convert pet-mask.png \
    pet-blurmask.png \
    -compose ColorBurn -composite \
    pet-transparencymask.png
[IM Output]
Of the 54 available compose operators, only these four do the right thing:
        Bumpmap Darken ColorBurn Multiply


7. (Wave the wand, Shazam!)  A final table look-up gets the transparency for each pixel of the image. See Computing the Transparency Table
 convert petEduc.png pet-transparencymask.png \
    ${PICTOOLS}/transparencytable.png -channel A \
    -fx 'u[2].p{v.r*10, u.intensity*10}.a' \
    -trim petEduc-nobg.png
[IM Output]

nosur-Make transparent the surrounding of the image

The key difference from the nobg script is to use floodfill to find only that portion of the background which reaches the outer edge of the image. To make sure that pockets are not isolated due to the image touching the edge, an early step surrounds the image with a one pixel border having the edge color.

1. Make a tile of the transparency color from the upper left pixel.
 convert pup-orig.jpg -crop 1x1+0+0 +repage \
    -scale 100x100 pup-tile.png
[IM Output]

2. Make an image one pixel larger in all directions and tile it with the transparency color.
 convert pup-orig.jpg -border 1 -flatten \
    -tile pup-tile.png -draw 'color 0,0 reset' \
    pup-blank.png
[IM Output]

3. Insert the original image into the result of step 2. Offset it so there is a border of transparency color all the way around. 
 convert pup-blank.png pup-orig.jpg \
    -geometry +1+1 -composite -matte pup-bordered.png
Every non-internal pixel of transparency color can now reach p{0,0} through other pixels of the same color.
[IM Output]

4. Subtract the transparency color image from the bordered image. Black indicates no difference. The lighter the color the greater the difference.
 convert pup-bordered.png pup-blank.png \
    -compose Difference -composite +matte \
    pup-diff.png
[IM Output]

5. Convert the image to grayscale and then pure black/white. The remaining black pizels are those that were within fuzzpct of the transparency color.
 convert pup-diff.png -colorspace Gray \
    -threshold 5% pup-mask.png
5% is the default for fuzz-percent.
[IM Output]

6a. Multiply all pixel values by .5. This does not affect black, but makes white gray.
6b. Floodfill all pixels that are reachable from the upper left corner. Make them all white.
 convert pup-mask.png -evaluate Multiply .5 \
    -fill white -draw 'color 0,0 floodfill' \
    pup-flooded.png
[IM Output]

There are now pixels of three colors in the mask: white for the transparent surround, gray for non-transparent, and black for interior pixels of the transparency color.
7. Threshold the image so white remains white and the other two become black. Negate the image to reverse the colors and make the transparency mask.
 convert pup-flooded.png -threshold 90% -negate \
    pup-surmask.png
[IM Output]

8. The rest is just like nobg. Blur the mask and combine it to retain all the black pixels.
 convert pup-surmask.png ( +clone -blur 10x2 ) \
    -compose ColorBurn -composite pup-blurredmask.png
[IM Output]

9. Apply the transparency table to the original image under control of the transparency mask from the previous step. Voila! The surround is transparent.
 convert pup-bordered.png pup-blurredmask.png \
    ${PICTOOLS}/transparencytable.png \
    -channel A -fx 'u[2].p{v.r*10, u.intensity*10}.a' \
    -trim pup-nosur.png
[IM Output]



Computing the Transparency Table

The transparency of a PNG image is dictated by the alpha channel. This is described in the PNG standard as:
The alpha sample determines a pixel's degree of opacity, where zero means fully transparent and the maximum value means fully opaque.

warning signempty space The usage of the alpha information internally within ImageMagick has changed from time to time. Version 6.3.0 is the opposite of earlier versions. Even now some parts of ImageMagick use "opacity" instead. It ranges from 0 for opaque to 1 for transparent. If you ask IM's identify command for the color of a pixel (with -format %[pixel:p{row,column}]) it will give you the opacity, instead of the transparency.

At the heart of the nobg and nosur algorithms is this computation:
 -fx 'u[2].p{v.r*10, u.intensity*10}.a'
This is applied against an image sequence of three images:
  • u, or u[0], the original image
  • v, or u[1], the transparency mask so laboriously constructed by prior operations
  • u[2], the "transparency table"
The expression is evaluated for each pixel of the original image (the first image in the sequence) and its value replaces the A channel of that image. After doing all pixels, -fx deletes all but the first image in the sequence.

The main thrust of the expression is to compute u[2].p{...}.a. This means to look at image u[2] (the transparency table), to extract a single pixel with .p, and to produce as the expression's value the .a part of that pixel. The .a part is the alpha channel value of the extracted pixel. The discussion below shows how that value is computed in the first place.

For pixel u[2].p{...}, the column is selected by the expression v.r*10. Here v, the middle image in the sequence, is the transparency mask.and v.r is the red value of that pixel (The mask is grayscale, so r, b, and g have the same value). The value of v.r ranges from 0 to 1. It is multiplied by ten to select a column in transparencytable.png.

In v, the transparency mask, pixels that should be transparent have the value 0; others that shold be opaque have a one. These show as black and white, respectively, in displays of these masks. Pixels just within the opaque part of the mask have values closer to zero, so they will be  mostly transparent, further in pixels have higher transparency values and will be more opaque. Only pixels within two or three of the transparent areas have intermediate values in the transparency mask.

The row of u[2].p{...} is selected based on the intensity of u, original image at the current pixel, via the expression u.intensity*10. The .intensity measures the perceived brightness of the pixel, ranging from 0 for black to 1 for white. The algorithm tries to make whiter colors more transparent. This is where the halo goes away. This is the magic. The table is such that there is more transparency for pixels as the intensity increases from top to bottom of transarencytable.png. The mutiplication by 10 is to convert the intensity value into an index into the table.

The transparency table looks like this (when expanded 10 to 1):

Transparency table - rows are for increasing color intensity, columns for increasing edge proximity

The color of this image is uniformly black, but some pixels have alpha values below one. Those pixels are transparent and let more or less of the background image be visible.

After considerable tinkering, the spread sheet values below were found to give resonable images. In this table, the salmon colored cells all exceed 1 and are fully opaque. The aritmetic of table construction was constrained so the two gold cells have the values that appear in them. These values are called mintr and maxtr, respectively.



Edge proximity  (0=transparent, 1=opqaue)


0 1 2 3 4 5 6 7 8 9 10
label: Color Intensity
0 -.01 -.01 .03 .12 .29 .58 1.01 1.61 2.41 3.43 4.71
1 -.01 -.01 .02 .11 .26 .52 .91 1.45 2.17 3.10 4.26
2 -.01 -.01 .02 .09 .24 .47 .82 1.31 1.96 2.79 3.83
3 -.01 -.01 .02 .08 .21 .42 .73 1.17 1.75 2.50 3.43
4 -.01 -.01 .01 .07 .19 .37 .65 1.04 1.56 2.23 3.06
5 -.01 -.01 .01 .06 .16 .33 .58 .93 1.39 1.98 2.72
6 -.01 -.01 .01 .06 .14 .29 .51 .82 1.23 1.75 2.41
7 -.01 -.01 .01 .05 .13 .26 .45 .72 1.08 1.54 2.12
8 -.01 -.01 .00 .04 .11 .22 .39 .63 .94 1.35 1.85
9 -.01 -.01 .00 .03 .09 .19 .34 .55 .82 1.17 1.61
10 -.01 -.01 .00 .03 .08 .16 .29 .47 .71 1.01 1.39

The parameters of the table are power and iDelta, which were finally decided to be best at 3 and 20, respectively. The expression for each cell of the table is then:
mintr+mul*((10+iDelta-j)*i)^power
where i and j are the column and row, respectively, and where mul is computed so that maxtr has the desired value. Mul turns out to have the value 0.000000174897119. The spread sheet went on to have a cell giving the exact -fx expression to be used in generating transparency table.png.

The Makefile section for generating the table looks like this:

transparencytable.png: Makefile
    convert -colorspace gray -size 11x11 -depth 8 \
           
xc:none -matte -channel A \
        -fx '-0.01+0.000000174897119*((30-j)*i)^3' \
        transparencytable.png


The line starting "convert" sets parameters for the image. It is created as having no color with xc:none, then that line goes on to say the image will have an alpha channel and the -fx will write into it. The final line says where to store the resulting image.

The spreadsheet also has a chart to depict the values of the expression. Its final shape is this:

graph of color intensity vs mask value, giving transparency value for each

Here Edge Proximity corresponds to columns in the table and Color Intensity to rows. The transparency value is the contents of the cell.



How-to with no Temporary Files


For scripts, it is inconvenient and time-consuming to write temporary images to file. Consequently I organized the scripts to keep needed versions of the image in memory. ImageMagick operates on a sequence of images, where some operators combine all the images of the sequence into one final image.

The parentheses operator delimits a section where some computation is done on a subsequence of images. The -clone operator copies from the outer sequence into thei inner sequence. After the closing parenthesis, the image(s) of the inner sequence are appended to the next outer sequence. (In this work there is only one image left from an inner sequence. Sometimes it is dicarded with -delete.)

In this section, the two scipts are broken down into their steps. A final +append at the end of a step produces an image which is the concatenation of all images in all existing sequences.

nobg - Make transparent all pixels of the background color

1a. Make enough copies of the original image for all the subsequent steps that need it.
1b. Make a tile of the transparent color.  The -crop selects the upper left pixel; the -scale expands it to 100x100. This larger size is meant to reduce the overhead in subsequent steps.
1c. Save the tile into internal file mpr:pixel (because the -tile command must read from a file).

 convert petEduc.png +matte \
    ( +clone ( +clone \
    ( +clone \
    -crop 1x1+0+0 +repage \
    -scale 100x100 \
    -write mpr:pixel ) \
  ...

[IM Output]

2. Throw away the tile from the end of the image sequence. Then specify to do tiling using that tile. The tiling is done by the "-draw" and its "reset" option causes all pixels to be replaced.

...
    +delete \
    -tile mpr:pixel -draw 'color 0,0 reset' ) \
...
[IM Output]

3. Subtract the transparent color from each pixel of the original image. (This uses the first copy of the image.)

...
    -compose Difference -composite \
...
[IM Output]

4a. Convert the difference image to grayscale without an alpha channel. Threshold the image so all values more than 5% as bright as white are converted to white. The image is now a mask.
4b. Make a copy of the mask and blur its edges.

...
    +matte -colorspace Gray -threshold 5% \
    ( +clone -blur 10x2 ) \
...
[IM Output]

5. Combine the mask and the blurred mask so blurring remains only in the parts that used to be completely white.

...
    -compose ColorBurn -composite ) \
...
[IM Output]

6. Read in a copy of the transparency table.

...
    ${PICTOOLS}/transparencytable.png \
...
[IM Output]

7a. Apply the transparency table and voila: the final image. The -channel operation restricts the -fx so it only sets the value for the alpha channel.
7b. Trim any edges that are wholly the transparency color.

...
-channel A \
    -fx 'u[2].p{v.r*10, u.intensity*10}.a' \
    -trim petEduc-nobg.png
[IM Output]

nosur- Make transparent the surrounding of the image

For nobg, the first step was to open a nested series of image sequences, each beginning with the original image. In nosur, the required image sequences do not all begin with the original image. Instead of many nested sequences, nosur has an outer sequence with saved images. For each sub-operation, an inner sequence is started and operand images are cloned from the outer sequence.

1. The first sub-operation creates a 100x100 tile of the transparency color. This tile is saved into an internal file for later use.
 convert pup-orig.jpg ( +clone -crop 1x1+0+0 \
    +repage -scale 100x100 -write mpr:pixel ) \
    ...
Note that the second of the two images in the sequence is a different width. In fact, it is the 100x100 image that has been stored in mpr:pixel.
[IM Output]

2a. Delete the image from the first sub-operation.
2b. The second sub-operation creates an image one pixel larger on all sides than the original image. This image is then covered over with the transparency tile from the internal file.
 ... +delete \
    ( +clone -border 1 -flatten \
    -tile mpr:pixel -draw 'color 0,0 reset' ) \
    ...
The "reset" parameter to draw says to replace all the pixels. The preceding -tile has set a parameter so that the coloring is done by tiling with the saved file (which is monochrome in the transparency color).
[IM Output]

3. The third sub-operation starts with the blank, bordered image just created and then overlays on it the original image. The geometry operator positions the image so its upper left is at the 1,1 pixel.
...
    ( -clone 1 -clone 0 -geometry +1+1 \
    -composite +matte ) \
    ...
[IM Output]

4a. The original image is no longer needed. We have it yet, but with a border of transparency colored pixels.
4b. Start the third suboperation by subtracting the blank image (-clone 0) from the bordered image (-clone 1).
... -delete 0 \
    ( -clone 1 -clone 0 \
    -compose Difference -composite +matte \
   ...
The -clone operation copies an image from the next outer image sequence. The the images are number from 0 for the first up to one less than the number of images in the sequence.
[IM Output]

5a. Process the difference to make a mask as shown above in steps 4,5, and 6 of the previous section. Make the difference image grayscale, convert to black/white, then to black/gray, then floodfill to get white/gray/black. Threshold back to white/black, and negate to get the outer parts black for transparency.
5b. Clone the image and blur its edges.
...
    -colorspace Gray -threshold 5% \
    -evaluate Multiply .5 \
    -fill white -draw 'color 0,0 floodfill' \
    -threshold 90% -negate \
    ( +clone -blur 10x2 ) \
 ...
[IM Output]

6. Combine the mask and blurred mask so the black from the mask is all black in the blurred image.
...
    -compose ColorBurn -composite \
    ) ...
[IM Output]

7. Finally, apply the transparency table to set the alpha channel of the image. Trim the image to get rid of the border and any additional transparent rows or columns.
... -delete 0 ${PICTOOLS}/transparencytable.png \
    -channel A -fx 'u[2].p{v.r*10, u.intensity*10}.a' \
    -trim pup-nosur.png
[IM Output]




The Effect of Blur-Spread

One of the options for nobg and nosur is
-b blur-spread
where blur-spread is a gaussian distribution. It is expressed as RxS indicating blur over a spot of radius R with a standard deviation of S.

This section shows the effect of various values for blur-spread. It does so with false color images showing the values generated. The colors were generated with

convert -size 11x1 xc:white \
    -fill 'white' -draw 'point 0,0' \
    -fill 'hsl(330,100,70)' -draw 'point 1,0' \
    -fill 'hsl(10,100,56)' -draw 'point 2,0' \
    -fill 'hsl(30,100,50)' -draw 'point 3,0' \
    -fill 'hsl(50,100,50)' -draw 'point 4,0' \
    -fill 'hsl(80,100,46)'  -draw 'point 5,0' \
    -fill 'hsl(110,100,50)' -draw 'point 6,0' \
    -fill 'hsl(170,100,46)' -draw 'point 7,0' \
    -fill 'hsl(210,100,50)' -draw 'point 8,0' \
    -fill 'hsl(240,100,60)' -draw 'point 9,0' \
    -fill 'hsl(270,100,50)' -draw 'point 10,0' \
    colorarray.png

(a value for 0,0 of hsl(290,100,85) would make its intensity consistent)


These colors correspond to 0.0, 0.1, ... 0.9, 1.0:  colors from 0 to 10
The 0.0 value is a wholly transparent background pixel. With the transparency algorithm described above, both the 0.9 and the 1.0 values map to entirely opaque pixels.

To simulate the mask, I made this black/white image, BW.png
BW.png, a simple black/white image
It has corners of both black and white and it has a "pit" into which it might be nice for transparency to probe.

The false color images were constructed with this script:
foreach b (   \
        2x.5 2x1 3x1   \
        3x1.5 4x1 4x2  \
        5x1 5x2 5x3    \
        10x1 10x2 10x3)

convert BW.png +matte \( +clone -blur $b \) \
    -compose ColorBurn -composite colorarray.png \
    -fx 'v.p{floor(10*u.intensity),0}'  blurMap-$b.png
    echo "<br>"
    echo "<br>"
    echo $b"<br>"
    echo "<img alt='map of blur $b' src='blurMap-$b.png' \
         style='width: 320px; height: 320px;'>"

end

Here are the images generated by the above command:
2x.5
map of blur 2x.5
2x1
map of blur 2x1
3x1
map of blur 3x1
3x1.5
map of blur 3x1.5
4x1
map of blur 4x1
4x2
map of blur 4x2
These colors correspond to 0.0, 0.1, ... 0.9, 1.0:  colors from 0 to 10
5x1
map of blur 5x1
5x2
map of blur 5x2
5x3
map of blur 5x3
10x1
map of blur 10x1
10x2
map of blur 10x2
10x3
map of blur 10x3


In general, the R pixels closest to an edge are affected, but only S+1 of them are given opacity less than one. For values with small S, like 2x.2 and 3x.3, virtually no blurring occurs; all pixels are either transparent or opaque.



The Complete Shell Scripts

Download the scripts

The script prefix

Since both scripts have the same options, they share most of their code. The nobg version of the common code follows. The nosur version is close to it.


#!/bin/sh

# prepare to give error failure
usage()  { echo "*** usage: nobg [-f fuzz-precent] \
            [-b blur-parameter] [-t] file [outfile]"
    exit 91 }


# initialize internal parameters
fuzzpct="5"
blur="10x2"
trim="-trim"
infile=""
outfile=""


# process command line switches
while [ $# != 0 ]; do  case "$1" in
  -f)    fuzzpct=$2 ; shift ;;
  -b)    blur=$2 ; shift ;;
  -t)    trim="" ;;    for nosur, trim=-shave 1x1"
  -*)    usage ;;
  *)    break ;;
  esac
  shift
done

      
# set infile and outfile from the remainder of the command line
if [ $# == 0 ]; then usage; fi
infile=$1
shift
if [ $# != 0 ]; then
    outfile=$1
    shift
else
    # generate outfile from infile
    outfile=${infile%.*}-nobg.png
fi
if [ $# != 0 ]; then usage; fi

nobg - Make transparent all pixels of the background color

      
convert $infile +matte  \( +clone                   \       
    \( +clone \( +clone -crop 1x1+0+0 +repage        \
        -scale 100x100 -write mpr:pixel \) +delete   \
        -tile mpr:pixel -draw 'color 0,0 reset' \)   \
    -compose Difference -composite                   \
    +matte -colorspace Gray -threshold $fuzzpct%    \
    \( +clone -blur $blur \)                        \
    -compose $compop -composite \)                  \
   ${PICTOOLS}/transparencytable.png                \
   -channel A -fx 'u[2].p{v.r*10, u.intensity*10}.a' \
   $trim $outfile

nosur- Make transparent the surrounding of the image

      
convert $infile                                      \
    \( +clone -crop 1x1+0+0 +repage -scale 100x100    \
        -write mpr:pixel \) +delete         
          \
    \( +clone -border 1 -flatten -tile mpr:pixel
      \
        -draw 'color 0,0 reset'  \)           
       \
    \( -clone 1 -clone 0 -geometry +1+1        
      \
        -composite +matte \)                 
        \
    -delete 0                           
             \
    \( -clone 1 -clone 0                 
            \
        -compose Difference -composite +matte   
     \
        -colorspace Gray -threshold $fuzzpct%   
    \
        -evaluate Multiply .5                
        \
        -fill white -draw 'color 0,0 floodfill'
       \
        -threshold 90% -negate                        \
        \( +clone -blur $blur \)             
       \
        -compose $compop -composite           
      \
    \)                                
               \
    -delete 0                          
              \
    ${PICTOOLS}/transparencytable.png  
             \
    -channel A -fx 'u[2].p{v.r*10, u.intensity*10}.a' \
    $trim $outfile



Download the Scripts

The halo scripts are now part of pictools. They have been converted to tcsh (because of bash's ridiculous approach to CRs).


Created:

22 February 2007 Two steins of beer
Updated:

 8 September 2007
Author:

Fred Hansen, <zweibieren@physpics.com>
ImageMagick:

6.3.0 11/05/06 Q16
URL:

http://physpics.com/pictools/halo/index.html