#!/bin/bash # B9 June 2017 # mktrans # This is similar to ImageMagick's bg_removal script, but much higher # quality. (It's also faster and simpler to use.) # # For a sample, run these commands: # convert logo: logo.png # mktrans logo.png # display logo-transparent.png # Fuzz is how far off the background color can be (in percent). # This is important for getting good antialiasing. defaultfuzz=20 usage() { cat <] [-s|-S] [-p ,] [-v] -f : How loosely to match the background color (default $defaultfuzz%) Advanced options: -s: Use speedy antialiasing (much faster, slightly less acurate) -S: Supress antialiasing completely. (Useful for repeated runs) p ,: Floodfill from pixel at x,y instead of 0,0 -v: Verbose EOF # * Side note: This creates an antialiased (blurred) alpha channel # that is also eroded by half a pixel to avoid halos. ImageMagick's # morphological operations don't (yet?) work at the subpixel level, # so I'm blowing up the alpha channel to 200% before eroding. Since # this can be slow on large images, consider using the '-s' option # which uses a faster, lower quality antialiasing. # * Running this script on an image that already has transparency will # erode the image due to the antialiasing. Using -S is a workaround, # but is not very satisfactory. Perhaps this script should remove any # existing transparency before manipulating the image and then add it # back in at the end. But then again, how often are people going to # want to do that? The only use I can think of is when using -p. # * Because of the previous bug, if you do use -p to fill lots of # lagoons, you'll probably want to use -A at the same time. # * Finding the coordinates for -p is a pain. It'd be nice if there was # a -P option which let the user click on a point (or multiple points) # in the image to start the floodfill. exit 0 } fuzz=$defaultfuzz pixelcomma="0,0" pixelplus="+0+0" while getopts f:sAShp:v name; do case $name in f) fuzz=$OPTARG ;; s) sflag=True ;; S|A) noantialias=True ;; v) vflag=True ;; h) usage ;; p) pixelcomma=$OPTARG pixelplus=+${OPTARG%,*}+${OPTARG#*,} pflag=True ;; *) usage ;; esac done shift $((OPTIND-1)) [[ "$#" != 0 ]] || usage for filename; do # Get color of 0,0 (top left) pixel color=$(convert "$filename" -format "%[pixel:p{$pixelcomma}]" info:-) if [[ "$color" == *rgba*",0)" ]]; then color="${color%,0)},1)" # Floodfill only works with opaque colors. fi if [[ "$color" == "none" ]]; then echo "Error: $filename: pixel at $pixelcomma is completely transparent. Cannot floodfill." >&2 continue fi options="" if [ -z "$pflag" ]; then # Add a 1 pixel border so we'll fill from the bottom and sides as well. options+=" -bordercolor $color -border 1 " fi # In a new stack, make a copy of the image options+=" ( +clone " # [copy] floodfill with transparency ("none") starting at top-left options+=" -fuzz $fuzz% -fill none -floodfill $pixelplus $color" # [copy] extract just the transparency (alpha channel) options+=" -alpha extract" if [ -z "$noantialias" ]; then if [ -z "$sflag" ]; then # [copy] blow up the alpha channel so we can do sub-pixel morphology options+=" -geometry 200%" # [copy] blur the alpha channel to make it antialiased options+=" -blur 0x0.5" # [copy] shrink the region that is opaque by half a pixel. options+=" -morphology erode square:1" # [copy] scale the alpha channel back to normal size. options+=" -geometry 50%" else # sflag: speedy antialias # [copy] blur the alpha channel to make it antialiased options+=" -blur 0x1" # [copy] only antialias inside the figure (<50% opacity becomes 0%) options+=" -level 50%,100%" fi fi # [copy] end the stack. options+=" ) " # Compose the original image and the copy's alpha channel. options+=" -compose CopyOpacity -composite" if [ -z "$pflag" ]; then # Remove the 1 pixel border we added options+=" -shave 1" fi [ "$vflag" ] && echo convert "$filename" $options "${filename%.*}-transparent.png" convert "$filename" $options "${filename%.*}-transparent.png" done